package com.huawang.mdesign.model.service.inter;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.huawang.business.constants.*;
import com.huawang.business.dto.ModelPropertyInfo;
import com.huawang.business.dto.RemoveModelDto;
import com.huawang.business.dto.UpdateModelDTO;
import com.huawang.business.dto.graph.ConnectInfo;
import com.huawang.business.dto.graph.ModelInfo;
import com.huawang.business.dto.graph.ValidateEndInfo;
import com.huawang.business.entity.matrix.DiagramMatrixCellNode;
import com.huawang.business.entity.matrix.DiagramMatrixRelationNode;
import com.huawang.business.exception.AbortExecutionException;
import com.huawang.business.service.customization.CustomizationService;
import com.huawang.business.service.model.base.ElementModelInfoService;
import com.huawang.business.service.model.base.ModelManagerConfig;
import com.huawang.business.service.model.base.UmlService;
import com.huawang.business.service.model.core.InternalElementTree;
import com.huawang.business.service.model.core.InternalRelationService;
import com.huawang.business.service.model.core.InternalUmlService;
import com.huawang.business.service.model.core.ModelPropertyInfoService;
import com.huawang.business.service.model.core.add.operation.CreateMessage;
import com.huawang.business.service.transaction.TransactionServiceImpl;
import com.huawang.business.util.InstanceUtils;
import com.huawang.business.util.InstanceVoUtils;
import com.huawang.business.vo.ModelResultVo;
import com.huawang.business.vo.RelationshipVo;
import com.huawang.business.vo.SelectElementNode;
import com.huawang.core.definition.mof.UMLDefinitionConstants;
import com.huawang.core.definition.mof.cmof.reflection.impl.BasicRElement;
import com.huawang.core.definition.mof.instances.MetaClassInstance;
import com.huawang.core.definition.mof.kernel.structureClassifiers.MClass;
import com.huawang.core.semantics.constants.RelationConstraints;
import com.huawang.core.semantics.entity.queryResult.MofResultContainer;
import com.huawang.core.semantics.entity.queryResult.QueryPropertyForm;
import com.huawang.core.semantics.entity.script.ElementType;
import com.huawang.core.semantics.entity.transaction.LocalTransaction;
import com.huawang.core.semantics.exception.LocalTransactionNotStartException;
import com.huawang.core.semantics.exception.TransactionNotExistingException;
import com.huawang.core.semantics.service.query.IRelationService;
import com.huawang.mdesign.model.service.dto.*;
import com.huawang.mdesign.model.service.util.LogUtil;
import com.huawang.mdesign.model.service.vo.*;
import com.huawang.mdesign.model.service.vo.selectelementitem.SelectElementItem;
import com.huawang.mdesign.model.service.facade.dto.AssociationEndInfo;
import com.huawang.mdesign.model.service.facade.util.CustomizationModelService;
import com.huawang.mdesign.model.service.facade.validate.common.UAFValidateUtils;
import lombok.Data;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;

import javax.validation.Valid;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.huawang.business.constants.ModelActionName.UPDATE_MODEL;

@Service
@Primary
@Slf4j
public class RelationshipService {
    public static final String ASSOCIATION_END_ERROR = "Association两端类型错误，Association Id为[%s]";
    public static final String RELATION_TYPE_ERROR = "未知Relation类型，Relation Id为[%s]";
    public static final String ASSOCIATION_BLOCK = "AssociationBlock";

    @Autowired
    private InternalUmlService internalUmlService;

    @Autowired
    private IRelationService iRelationService;

    @Autowired
    private UmlService umlService;

    @Autowired
    private ModelPropertyInfoService modelPropertyInfoService;

    @Autowired
    private CustomizationModelService customizationModelService;

    @Autowired
    private ModelManagerConfig managerConfig;

    @Autowired
    private ElementModelInfoService elementModelInfoService;

    @Autowired
    private UAFValidateUtils uafValidateUtils;

    @Autowired
    private DiagramConfigService diagramConfigService;

    @Autowired
    private CreateMessage createMessage;

    @Autowired
    private InternalRelationService internalRelationService;

    @Autowired
    private InternalElementTree internalElementTree;

    @Autowired
    private TransactionServiceImpl transactionService;

    @Autowired
    private CustomizationService customizationService;

    public static final String INCOMING = "incoming";
    public static final String OUTGOING = "outgoing";
    public static final String BOTH = "both";
    public static final String PACKAGE = "package";
    public static final String IN = "in";

    @NotNull
    public List<RelationshipVo> queryRelationship(QueryRelationshipDTO queryRelationshipDto) {
        LogUtil.printSerializeOnlineLogs("queryRelationshipDto: ", queryRelationshipDto);
        final String projectId = queryRelationshipDto.getProjectId();
        List<MetaClassInstance> relationships = internalUmlService.queryRelationship(projectId,
                        queryRelationshipDto.getFirstEndId(), queryRelationshipDto.getSecondEndIds()).stream().distinct()
                .collect(Collectors.toList());

        final String firstEndId = queryRelationshipDto.getFirstEndId();
        final List<String> secondEndIds = queryRelationshipDto.getSecondEndIds();
        Map<String, MetaClassInstance> instanceMap = internalUmlService.fetchInstances(
                Stream.concat(Stream.of(firstEndId), secondEndIds.stream()).collect(Collectors.toList()), projectId);

        MetaClassInstance first = instanceMap.get(firstEndId);
        if (first == null) {
            return Collections.emptyList();
        }

        QueryPropertyForm queryPropertyForm = internalUmlService.getQueryPropertyForm(
                new ArrayList<>(instanceMap.values()), UMLDefinitionConstants.NamedElement_namespace);
        MofResultContainer endResult = internalUmlService.queryProperty(projectId, queryPropertyForm);

        MetaClassInstance firstNamespace = endResult.queryInstanceProperty(
                first.getId(), UMLDefinitionConstants.NamedElement_namespace);

        List<RelationshipVo> relationshipVos = queryRelationshipDto.getSecondEndIds().stream().map(secondEndId -> {
            MetaClassInstance secondEnd = instanceMap.get(secondEndId);
            if (!(internalUmlService.checkInstanceType(projectId, secondEnd, UMLDefinitionConstants.Classifier)
                    || internalUmlService.checkInstanceType(projectId, secondEnd, UMLDefinitionConstants.Package))) {
                return null;
            }

            if (!internalUmlService.checkInstanceType(projectId, first, UMLDefinitionConstants.Property)
                    && !internalUmlService.checkInstanceType(projectId, first, UMLDefinitionConstants.Port)) {
                if (InstanceUtils.instanceEqual(firstNamespace, secondEnd)) {
                    RelationshipVo relationshipVo = new RelationshipVo();
                    relationshipVo.setSourceId(queryRelationshipDto.getFirstEndId());
                    relationshipVo.setTargetId(secondEndId);
                    relationshipVo.setInstanceId(ModelConstantURI.CONTAINMENT);
                    return relationshipVo;
                } else {
                    MetaClassInstance secondEndOwner = endResult.queryInstanceProperty(secondEnd.getId(),
                            UMLDefinitionConstants.NamedElement_namespace);
                    if (InstanceUtils.instanceEqual(secondEndOwner, first)) {
                        RelationshipVo relationshipVo = new RelationshipVo();
                        relationshipVo.setSourceId(secondEndId);
                        relationshipVo.setTargetId(queryRelationshipDto.getFirstEndId());
                        relationshipVo.setInstanceId(ModelConstantURI.CONTAINMENT);
                        return relationshipVo;
                    }
                }
            }
            return null;
        }).filter(Objects::nonNull).collect(Collectors.toList());
        relationshipVos.addAll(relationships.stream()
                .map(relationship -> fillRelationElement(projectId, relationship))
                .collect(Collectors.toList()));

        relationshipVos.addAll(findAnchor(projectId, first));

        return relationshipVos;
    }

    private List<RelationshipVo> getContainment(String projectId, Map<String, MetaClassInstance> firstEndIds, Map<String, MetaClassInstance> secondEndIds) {
        QueryPropertyForm queryPropertyForm = internalUmlService.getQueryPropertyForm(Lists.newArrayList(firstEndIds.values()), UMLDefinitionConstants.NamedElement_namespace);
        MofResultContainer mofResultContainer = internalUmlService.queryProperty(projectId, queryPropertyForm);
        List<RelationshipVo> relationshipVos = Lists.newArrayList();
        for (MetaClassInstance instance : firstEndIds.values()) {
            if (!(internalUmlService.checkInstanceType(projectId, instance, UMLDefinitionConstants.Classifier) || internalUmlService.checkInstanceType(projectId, instance, UMLDefinitionConstants.Package))) {
                continue;
            }
            MetaClassInstance namespace = mofResultContainer.queryInstanceProperty(instance.getId(), UMLDefinitionConstants.NamedElement_namespace);
            if (namespace != null && secondEndIds.containsKey(namespace.getId())) {
                RelationshipVo relationshipVo = new RelationshipVo();
                relationshipVo.setSourceId(instance.getId());
                relationshipVo.setTargetId(namespace.getId());
                relationshipVo.setInstanceId(ModelConstantURI.CONTAINMENT);
                relationshipVos.add(relationshipVo);
            }
        }
        return relationshipVos;
    }

    /**
     * 查找注释线：元素是一个Comment，通过属性Comment_annotatedElement查找注释线；
     * 其它类型的元素通过A_annotatedElement_comment_comment属性查找注释线。
     *
     * @param projectId
     * @param first
     * @return java.util.List<com.huawang.mdesign.model.service.vo.RelationshipVo>
     * @author baibf
     * @date 2022/07/29 11:10
     */
    @NotNull
    private List<RelationshipVo> findAnchor(String projectId, MetaClassInstance first) {
        LogUtil.printSerializeOnlineLogs("projectId: {}, first: {}", projectId, first);
        if (internalUmlService.checkInstanceType(projectId, first, UMLDefinitionConstants.Comment)) {
            List<MetaClassInstance> annotatedElements = internalUmlService.queryListInstanceProperty(projectId, first,
                    UMLDefinitionConstants.Comment_annotatedElement);
            return convertAnnotatedElementToRelationships(first, annotatedElements);
        } else {
            List<MetaClassInstance> comments = internalUmlService.queryListInstanceProperty(projectId, first,
                    UMLDefinitionConstants.A_annotatedElement_comment_comment);
            return convertAnnotatedElementToRelationships(first, comments);
        }
    }

    @NotNull
    private static List<RelationshipVo> convertAnnotatedElementToRelationships(MetaClassInstance first, List<MetaClassInstance> annotatedElements) {
        LogUtil.printSerializeOnlineLogs("first: {}, annotatedElements: {}", first, annotatedElements);
        if (CollectionUtils.isEmpty(annotatedElements)) {
            return Collections.emptyList();
        }

        return annotatedElements.stream().map(annotatedElement -> {
            RelationshipVo relationshipVo = new RelationshipVo();
            relationshipVo.setSourceId(first.getId());
            relationshipVo.setTargetId(annotatedElement.getId());
            relationshipVo.setInstanceId(ModelConstantURI.ANCHOR);
            return relationshipVo;
        }).collect(Collectors.toList());
    }

    /**
     * 构建模型上关系两端的关联元素
     *
     * @param projectId
     * @param relationship
     * @return
     */
    @NotNull
    public RelationshipVo fillRelationElement(String projectId, MetaClassInstance relationship) {
        LogUtil.printSerializeOnlineLogs("projectId: {}, relationship: {}", projectId, relationship);
        RelationshipVo vo = new RelationshipVo();
        MetaClassInstance source;
        MetaClassInstance target;
        boolean isDirect = true;
        if (internalUmlService.checkInstanceType(projectId, relationship, UMLDefinitionConstants.DirectedRelationship)) {
            List<MetaClassInstance> metaClassInstances = internalUmlService.queryListInstanceProperty(projectId, relationship, UMLDefinitionConstants.DirectedRelationship_source);
            if (!metaClassInstances.isEmpty()) {
                source = metaClassInstances.get(0);
            } else {
                source = null;
            }
            List<MetaClassInstance> metaClassInstances1 = internalUmlService.queryListInstanceProperty(projectId, relationship, UMLDefinitionConstants.DirectedRelationship_target);
            if (!metaClassInstances1.isEmpty()) {
                target = metaClassInstances1.get(0);
            } else {
                target = null;
            }
        } else if (internalUmlService.checkInstanceType(projectId, relationship, UMLDefinitionConstants.Association)) {
            List<MetaClassInstance> endTypes = internalUmlService.queryListInstanceProperty(projectId, relationship,
                    UMLDefinitionConstants.Association_endType);
            if (CollectionUtils.isEmpty(endTypes)) {
                log.warn(String.format(ASSOCIATION_END_ERROR, relationship.getId()));
                return vo;
            }

            if (endTypes.size() == 1) {
                source = endTypes.get(0);
                target = source;
            } else {
                source = endTypes.get(1);
                target = endTypes.get(0);
            }

            List<MetaClassInstance> ownedEnd = internalUmlService.queryListInstanceProperty(projectId, relationship,
                    UMLDefinitionConstants.Association_ownedEnd);
            isDirect = CollectionUtils.isNotEmpty(ownedEnd);
            vo.setIsAllInRelation(ownedEnd.size() == 2);
            vo.setAssociationType(elementModelInfoService.fetchAssociationValue(relationship, projectId));
        } else if (internalUmlService.checkInstanceType(projectId, relationship, UMLDefinitionConstants.Transition)) {
            source = internalUmlService.queryInstanceProperty(projectId, relationship, UMLDefinitionConstants.Transition_source);
            target = internalUmlService.queryInstanceProperty(projectId, relationship, UMLDefinitionConstants.Transition_target);
        } else if (internalUmlService.checkInstanceType(projectId, relationship, UMLDefinitionConstants.ActivityEdge)) {
            source = internalUmlService.queryInstanceProperty(projectId, relationship, UMLDefinitionConstants.ActivityEdge_source);
            target = internalUmlService.queryInstanceProperty(projectId, relationship, UMLDefinitionConstants.ActivityEdge_target);
        } else if (internalUmlService.checkInstanceType(projectId, relationship, UMLDefinitionConstants.Connector)) {
            List<MetaClassInstance> connectorEnd = internalUmlService.queryListInstanceProperty(projectId, relationship, UMLDefinitionConstants.Connector_end);
            source = internalUmlService.queryInstanceProperty(projectId, connectorEnd.get(0),
                    UMLDefinitionConstants.ConnectorEnd_role);
            target = internalUmlService.queryInstanceProperty(projectId, connectorEnd.get(1),
                    UMLDefinitionConstants.ConnectorEnd_role);
            isDirect = false;
        } else if (internalUmlService.checkInstanceType(projectId, relationship,
                UMLDefinitionConstants.ExceptionHandler)) {
            source = internalUmlService.queryInstanceProperty(projectId, relationship,
                    UMLDefinitionConstants.ExceptionHandler_protectedNode);
            target = internalUmlService.queryInstanceProperty(projectId, relationship,
                    UMLDefinitionConstants.ExceptionHandler_handlerBody);
        } else {
            log.warn(String.format(RELATION_TYPE_ERROR, relationship.getId()));
            return vo;
        }

        List<MetaClassInstance> stereotypes = internalUmlService.queryListInstanceProperty(projectId, relationship, UMLDefinitionConstants.Element_appliedStereotype);
        vo.setBaseStereotype(internalUmlService.queryBaseStereotype(projectId, stereotypes));
        vo.setInstanceId(relationship.getId());
        MClass classifier = relationship.getClassifier();
        vo.setMetaClass(classifier.getName());
        if (source != null) {
            vo.setSourceId(source.getId());
            vo.setSourceName(internalUmlService.queryStringProperty(projectId, source,
                    UMLDefinitionConstants.NamedElement_name));
        }
        if (target != null) {
            vo.setTargetId(target.getId());
            vo.setTargetName(internalUmlService.queryStringProperty(projectId, target,
                    UMLDefinitionConstants.NamedElement_name));
        }

        vo.setIsDirection(isDirect);
        return vo;
    }

    /**
     * 构建模型上关系两端的关联元素
     *
     * @param projectId
     * @param model
     * @return
     */
    @NotNull
    public RelationshipVo fillRelationElementByModel(String projectId, ModelPropertyInfo model) {
        LogUtil.printOnlineLogs("projectId: ", projectId);
        final MetaClassInstance relationship = model.getInstance();
        RelationshipVo vo = new RelationshipVo();
        MetaClassInstance source = null;
        MetaClassInstance target = null;
        boolean isDirect = true;
        if (model.checkInstanceType(UMLDefinitionConstants.DirectedRelationship)) {
            source = getRelationshipEndFirst(model.queryListInstanceValue(UMLDefinitionConstants.DirectedRelationship_source));
            target = getRelationshipEndFirst(model.queryListInstanceValue(UMLDefinitionConstants.DirectedRelationship_target));
        } else if (model.checkInstanceType(UMLDefinitionConstants.Association)) {
            AssociationEndInfo associationEndInfo = getAssociationEndInfo(projectId, model, relationship);
            if (CollectionUtils.isEmpty(associationEndInfo.getEndTypes())) {
                log.warn(String.format(ASSOCIATION_END_ERROR, relationship.getId()));
                return vo;
            }
            source = associationEndInfo.getSource();
            target = associationEndInfo.getTarget();
            List<MetaClassInstance> ownedEnd = model.queryListInstanceValue(UMLDefinitionConstants.Association_ownedEnd);
            isDirect = internalUmlService.isDirectOfAssociation(projectId, model.getInstance());
            vo.setIsAllInRelation(ownedEnd.size() == 2);
            vo.setAssociationType(elementModelInfoService.fetchAssociationValue(relationship, projectId, isDirect));
        } else if (model.checkInstanceType(UMLDefinitionConstants.Transition)) {
            source = model.queryInstanceValue(UMLDefinitionConstants.Transition_source);
            target = model.queryInstanceValue(UMLDefinitionConstants.Transition_target);
        } else if (model.checkInstanceType(UMLDefinitionConstants.ActivityEdge)) {
            source = model.queryInstanceValue(UMLDefinitionConstants.ActivityEdge_source);
            target = model.queryInstanceValue(UMLDefinitionConstants.ActivityEdge_target);
        } else if (model.checkInstanceType(UMLDefinitionConstants.Connector)) {
            List<MetaClassInstance> connectorEnd = model.queryListInstanceValue(UMLDefinitionConstants.Connector_end);
            source = internalUmlService.queryInstanceProperty(projectId, connectorEnd.get(0),
                    UMLDefinitionConstants.ConnectorEnd_role);
            target = internalUmlService.queryInstanceProperty(projectId, connectorEnd.get(1),
                    UMLDefinitionConstants.ConnectorEnd_role);
            isDirect = false;
        } else if (model.checkInstanceType(UMLDefinitionConstants.ExceptionHandler)) {
            source = model.queryInstanceValue(UMLDefinitionConstants.ExceptionHandler_protectedNode);
            target = model.queryInstanceValue(UMLDefinitionConstants.ExceptionHandler_handlerBody);
        } else if (model.checkInstanceType(UMLDefinitionConstants.Message)) {
            MetaClassInstance messageOccurrenceSpecification = model.queryInstanceValue(UMLDefinitionConstants.Message_sendEvent);
            List<MetaClassInstance> lifeLines;
            if(messageOccurrenceSpecification != null) {
                lifeLines = internalUmlService.queryListInstanceProperty(projectId, messageOccurrenceSpecification, UMLDefinitionConstants.OccurrenceSpecification_covered);
                if(!lifeLines.isEmpty()) {
                    MetaClassInstance represents = internalUmlService.queryInstanceProperty(projectId, lifeLines.get(0), UMLDefinitionConstants.Lifeline_represents);
                    source = internalUmlService.queryInstanceProperty(projectId, represents, UMLDefinitionConstants.TypedElement_type);
                }
            }
            messageOccurrenceSpecification = model.queryInstanceValue(UMLDefinitionConstants.Message_receiveEvent);
            if(messageOccurrenceSpecification != null) {
                lifeLines = internalUmlService.queryListInstanceProperty(projectId, messageOccurrenceSpecification, UMLDefinitionConstants.OccurrenceSpecification_covered);
                if(!lifeLines.isEmpty()) {
                    MetaClassInstance represents = internalUmlService.queryInstanceProperty(projectId, lifeLines.get(0), UMLDefinitionConstants.Lifeline_represents);
                    target = internalUmlService.queryInstanceProperty(projectId, represents, UMLDefinitionConstants.TypedElement_type);
                }
            }
        } else {
            log.warn(String.format(RELATION_TYPE_ERROR, relationship.getId()));
            return vo;
        }

        vo.setBaseStereotype(model.getBaseStereotype());
        vo.setInstanceId(relationship.getId());
        vo.setMetaClass(relationship.getClassifier().getName());
        fillVoSource(projectId, model, vo, source);
        fillVoTarget(projectId, model, vo, target);
        vo.setIsDirection(isDirect);
        return vo;
    }

    @NotNull
    private AssociationEndInfo getAssociationEndInfo(String projectId, ModelPropertyInfo model, MetaClassInstance relationship) {
        LogUtil.printSerializeOnlineLogs("projectId: , relationship: ", projectId, relationship);
        AssociationEndInfo associationEndInfo = new AssociationEndInfo();
        List<MetaClassInstance> endTypes = model.queryListInstanceValue(UMLDefinitionConstants.Association_endType);
        if (CollectionUtils.isEmpty(associationEndInfo.getEndTypes())) {
            log.warn(String.format(ASSOCIATION_END_ERROR, relationship.getId()));
            return associationEndInfo;
        }

        if (endTypes.size() == 1) {
            associationEndInfo.setSource(endTypes.get(0));
            associationEndInfo.setTarget(associationEndInfo.getSource());
        } else if (endTypes.size() == 2) {
            List<MetaClassInstance> ownedEndList = internalUmlService.queryListInstanceProperty(projectId,
                    relationship, UMLDefinitionConstants.Association_ownedEnd);
            if (ownedEndList.isEmpty()) {
                associationEndInfo.setSource(endTypes.get(1));
                associationEndInfo.setTarget(endTypes.get(0));
            } else if (ownedEndList.size() == 1) {
                associationEndInfo.setSource(internalUmlService.queryInstanceProperty(projectId, ownedEndList.get(0),
                        UMLDefinitionConstants.TypedElement_type));
                endTypes.remove(associationEndInfo.getSource());
                associationEndInfo.setTarget(endTypes.get(0));
            } else {
                log.info("关联模型{}的Association_ownedEnd属性数量 > 1, source和target为空", relationship.getId());
            }

        } else {
            List<MetaClassInstance> ownedEndList = internalUmlService.queryListInstanceProperty(projectId,
                    relationship, UMLDefinitionConstants.Association_ownedEnd);
            associationEndInfo.setSource(getAssociationSource(projectId, endTypes, ownedEndList));
            List<MetaClassInstance> memberEndList = internalUmlService.queryListInstanceProperty(projectId,
                    relationship, UMLDefinitionConstants.Association_memberEnd);
            memberEndList.removeAll(ownedEndList);
            if (CollectionUtils.isNotEmpty(memberEndList)) {
                associationEndInfo.setTarget(internalUmlService.queryInstanceProperty(projectId, memberEndList.get(0),
                        UMLDefinitionConstants.TypedElement_type));
            }
        }
        return associationEndInfo;
    }

    @Nullable
    private MetaClassInstance getAssociationSource(String projectId, List<MetaClassInstance> endTypes, List<MetaClassInstance> ownedEndList) {
        LogUtil.printSerializeOnlineLogs("projectId: {}, endTypes: {}, ownedEndList: {}", projectId, endTypes, ownedEndList);
        MetaClassInstance source;
        if (CollectionUtils.isNotEmpty(ownedEndList)) {
            source = internalUmlService.queryInstanceProperty(projectId, ownedEndList.get(0),
                    UMLDefinitionConstants.TypedElement_type);
        } else {
            source = endTypes.get(1);
        }
        return source;
    }

    private void fillVoTarget(String projectId, ModelPropertyInfo model, RelationshipVo vo, MetaClassInstance target) {
        LogUtil.printSerializeOnlineLogs("projectId: {}, vo: {}, target: {}", projectId, vo, target);
        if (target != null) {
            vo.setTargetId(target.getId());
            vo.setTargetName(getEndPointName(projectId, model, target));
        }
    }

    private void fillVoSource(String projectId, ModelPropertyInfo model, RelationshipVo vo, MetaClassInstance source) {
        LogUtil.printSerializeOnlineLogs("projectId: {}, vo: {}, source: {}", projectId, vo, source);
        if (source != null) {
            vo.setSourceId(source.getId());
            vo.setSourceName(getEndPointName(projectId, model, source));
        }
    }

    @Nullable
    private static MetaClassInstance getRelationshipEndFirst(List<MetaClassInstance> metaClassInstances) {
        LogUtil.printSerializeOnlineLogs("metaClassInstances: ", metaClassInstances);
        if (!metaClassInstances.isEmpty()) {
            return metaClassInstances.get(0);
        }
        return null;
    }

    @Nullable
    private String getEndPointName(String projectId, ModelPropertyInfo model, MetaClassInstance endPointInstance) {
        LogUtil.printSerializeOnlineLogs("projectId: {}, endPointInstance: {}", projectId, endPointInstance);
        QueryPropertyForm queryPropertyForm = internalUmlService.getQueryPropertyForm(endPointInstance,
                Stream.of(UMLDefinitionConstants.NamedElement_name,
                        UMLDefinitionConstants.Lifeline_represents).collect(Collectors.toList()));
        MofResultContainer endPointResult = internalUmlService.queryProperty(projectId, queryPropertyForm);

        String endPointName = endPointResult.queryStringProperty(endPointInstance.getId(), UMLDefinitionConstants.NamedElement_name);
        if (StringUtils.isEmpty(endPointName) && (model.checkInstanceType(UMLDefinitionConstants.Abstraction)
                && internalUmlService.checkInstanceType(projectId, endPointInstance, UMLDefinitionConstants.Lifeline))) {
            final MetaClassInstance representsInstance = endPointResult.queryInstanceProperty(
                    endPointInstance.getId(), UMLDefinitionConstants.Lifeline_represents);
            endPointName = internalUmlService.queryStringProperty(projectId, representsInstance,
                    UMLDefinitionConstants.NamedElement_name);
        }
        return endPointName;
    }


    /**
     * 构建矩阵关系模型数据
     *
     * @param projectId
     * @return com.huawang.mdesign.model.service.vo.DiagramMatrixCellRelationVo
     * @author xz
     * @date 2022/07/05 18:03
     */
    @NotNull
    public DiagramMatrixCellRelationVo buildDiagramMatrixCellRelationVo(String projectId,
                                                                        @NotNull final DiagramMatrixCellNode cellNode,
                                                                        DiagramMatrixRelationNode relationNode,
                                                                        Map<String, List<MetaClassInstance>> listInstanceMap,
                                                                        String rowId,
                                                                        String columnId) {
        LogUtil.printSerializeOnlineLogs("projectId: {}, cellNode: {}, relationNode: {}, rowId: {}, columnId: {}", projectId,
                cellNode, relationNode, rowId, columnId);
        final MapOperationTypeEnum type = MapOperationTypeEnum.queryByValue(relationNode.getRelationType());
        if (type == null) {
            return new DiagramMatrixCellRelationVo();
        }

        switch (type) {
            case SIMPLE_NAVIGATION:
            case SIMPLE_NAVIGATION_COLLECTION:
                return buildSimpleNavigationVo(projectId, cellNode, relationNode, listInstanceMap, rowId, columnId);
            default:
                return buildOtherNavigation(relationNode, rowId, columnId);
        }
    }

    @NotNull
    public DiagramMatrixCellRelationVo buildDiagramMatrixCellRelationVo(@NotNull final DiagramMatrixCellNode cellNode,
                                                                        DiagramMatrixRelationNode relationNode,
                                                                        String rowId,
                                                                        String columnId) {
        LogUtil.printSerializeOnlineLogs("cellNode: {}, relationNode: {}, rowId: {}, columnId: {}", cellNode, relationNode,
                rowId, columnId);
        final MapOperationTypeEnum type = MapOperationTypeEnum.queryByValue(relationNode.getRelationType());
        if (type == null) {
            return new DiagramMatrixCellRelationVo();
        }

        if (Objects.equals(MapOperationTypeEnum.SIMPLE_NAVIGATION, type)) {
                return buildSimpleNavigationVo(cellNode, relationNode, rowId, columnId);
        }
        return buildOtherNavigation(relationNode, rowId, columnId);
    }

    @NotNull
    private static DiagramMatrixCellRelationVo buildOtherNavigation(DiagramMatrixRelationNode relationNode, String rowId, String columnId) {
        LogUtil.printSerializeOnlineLogs("relationNode: {}, rowId: {}, columnId: {}", relationNode, rowId, columnId);
        DiagramMatrixCellRelationVo vo = new DiagramMatrixCellRelationVo();
        vo.setKey(relationNode.getRelationKey());
        vo.setOperationType(relationNode.getRelationType());
        if (relationNode.getMatrixRelationDirection() == MatrixRelationDirection.COLUMN_TO_ROW) {
            vo.setSourceId(columnId);
            vo.setTargetId(rowId);
        } else {
            vo.setSourceId(rowId);
            vo.setTargetId(columnId);
        }

        if (relationNode.getMatrixRelationDirection() == MatrixRelationDirection.BOTH) {
            vo.setIsDirection(false);
        }

        return vo;
    }

    @NotNull
    private DiagramMatrixCellRelationVo buildSimpleNavigationVo(@NotNull final DiagramMatrixCellNode cellNode,
                                                                DiagramMatrixRelationNode relationNode,
                                                                String rowId,
                                                                String columnId) {
        LogUtil.printSerializeOnlineLogs("cellNode: {}, relationNode: {}, rowId: {}, columnId: {}", cellNode, relationNode,
                rowId, columnId);
        DiagramMatrixCellRelationVo vo = new DiagramMatrixCellRelationVo();
        final MetaClassInstance relationship = relationNode.getMetaClassInstance();
        if (relationship == null) {
            log.warn("矩阵简单关系模型不存在");
            return buildOtherNavigation(relationNode, rowId, columnId);
        }

        SourceTargetContext sourceTargetContext = findSourceTargetInstance(vo, relationNode, cellNode);
        vo.setInstanceId(relationship.getId());
        if (sourceTargetContext.getSource() != null) {
            vo.setSourceId(sourceTargetContext.getSource().getId());
        }
        if (sourceTargetContext.getTarget() != null) {
            vo.setTargetId(sourceTargetContext.getTarget().getId());
        }
        vo.setIsDirection(sourceTargetContext.isDirect());
        return vo;
    }

    @NotNull
    private DiagramMatrixCellRelationVo buildSimpleNavigationVo(String projectId,
                                                                @NotNull final DiagramMatrixCellNode cellNode,
                                                                DiagramMatrixRelationNode relationNode,
                                                                Map<String, List<MetaClassInstance>> listInstanceMap,
                                                                String rowId,
                                                                String columnId) {
        LogUtil.printSerializeOnlineLogs("projectId: {}, cellNode: {}, relationNode: {}, rowId: {}, columnId； {}", projectId,
                cellNode, relationNode, rowId, columnId);
        DiagramMatrixCellRelationVo vo = new DiagramMatrixCellRelationVo();
        final MetaClassInstance relationship = relationNode.getMetaClassInstance();
        if (relationship == null) {
            log.warn("矩阵简单关系模型不存在");
            return buildOtherNavigation(relationNode, rowId, columnId);
        }

        SourceTargetContext sourceTargetContext = findSourceTargetInstance(vo, relationNode, cellNode);
        if (internalUmlService.checkInstanceType(projectId, relationNode.getMetaClassInstance(), UMLDefinitionConstants.Association)) {
            if (listInstanceMap == null) {
                sourceTargetContext.setDirect(false);
            }
            if (listInstanceMap != null) {
                List<MetaClassInstance> endTypes = listInstanceMap.get(UMLDefinitionConstants.Association_endType);
                if (CollectionUtils.isEmpty(endTypes) || endTypes.size() > 2) {
                    log.warn(String.format(ASSOCIATION_END_ERROR, relationship.getId()));
                    return vo;
                } else if (endTypes.size() == 1) {
                    sourceTargetContext.setSource(endTypes.get(0));
                    sourceTargetContext.setTarget(endTypes.get(0));
                } else if (endTypes.size() == 2) {
                    sourceTargetContext.setSource(endTypes.get(1));
                    sourceTargetContext.setTarget(endTypes.get(0));
                }
                List<MetaClassInstance> ownedEnd = listInstanceMap.get(UMLDefinitionConstants.Association_ownedEnd);
                if (CollectionUtils.isEmpty(ownedEnd)) {
                    sourceTargetContext.setDirect(false);
                }
            }
        }
        if (internalUmlService.checkInstanceType(projectId, relationship, UMLDefinitionConstants.Connector)) {
            sourceTargetContext.setDirect(false);
        }
        vo.setInstanceId(relationship.getId());
        if (sourceTargetContext.getSource() != null) {
            vo.setSourceId(sourceTargetContext.getSource().getId());
        }
        if (sourceTargetContext.getTarget() != null) {
            vo.setTargetId(sourceTargetContext.getTarget().getId());
        }

        vo.setIsDirection(sourceTargetContext.isDirect());
        return vo;
    }

    @Nullable
    public SelectElementNode queryValidateEnds(QueryRelationshipEndDTO dto) {
        LogUtil.printSerializeOnlineLogs("queryValidateEnds -> dto: ", dto);
        final String projectId = dto.getProjectId();

        final String firstEndId = dto.getFirstEndId();
        final String relationshipType = dto.getRelationshipType();
        Map<String, MetaClassInstance> instanceMap = internalUmlService.fetchInstances(
                Stream.of(firstEndId, relationshipType).collect(Collectors.toList()), projectId);

        MetaClassInstance sourceInstance = instanceMap.get(firstEndId);
        MetaClassInstance relation = instanceMap.get(relationshipType);
        if (sourceInstance == null || relation == null) {
            return null;
        }

        List<MetaClassInstance> allInstance;
        if (dto.getRelationshipType().equals(UMLDefinitionConstants.Generalization)) {
            String metaclass = sourceInstance.getMetaClass().getId();

            allInstance = internalUmlService.querySpecifiedInstanceList(projectId, metaclass).stream().filter(it ->
                    Objects.equals(it.getMetaClass().getId(), metaclass)).collect(Collectors.toList());
            allInstance.remove(sourceInstance);
        } else {
            List<String> metaclassList;
            ModelInfo info = internalUmlService.fetchSpecificModelInfoValue(projectId, relation);
            if (info != null) {
                Set<String> outs;
                ValidateEndInfo validateEnds = customizationModelService.getValidateEndInfo(dto.getProjectId(), info.getValue());
                if (dto.getDirection().equals(IN)) {
                    outs = validateEnds.getIncoming();
                } else {
                    outs = validateEnds.getOutgoing();
                }

                metaclassList = outs.stream().map(it -> managerConfig.getModelInfoConfig().get(it))
                        .filter(Objects::nonNull)
                        .map(this::getTypeIdByModelInfo)
                        .distinct()
                        .collect(Collectors.toList());
            } else {
                metaclassList = internalUmlService.queryRelationEndType(projectId, sourceInstance,
                                dto.getRelationshipType(), dto.getDirection())
                        .stream().map(BasicRElement::getId).collect(Collectors.toList());
            }

            if (CollectionUtils.isEmpty(metaclassList)) {
                return null;
            }

            allInstance = internalUmlService.querySpecifiedInstanceList(projectId,
                    new HashSet<>(metaclassList), false);
            if (dto.getRelationshipType().equals(UMLDefinitionConstants.PackageImport)
                    || dto.getRelationshipType().equals(UMLDefinitionConstants.ElementImport)) {
                allInstance.remove(sourceInstance);
            }
        }

        return internalElementTree.parseListToTree(projectId, allInstance, false);
    }

    @NotNull
    public List<String> queryValidateEndTypes(QueryRelationshipEndDTO dto) {
        LogUtil.printSerializeOnlineLogs("queryValidateEndTypes -> dto: ", dto);
        final String projectId = dto.getProjectId();
        MetaClassInstance sourceInstance = internalUmlService.fetchInstance(dto.getFirstEndId(), projectId);
        if (sourceInstance == null) {
            return Collections.emptyList();
        }

        MetaClassInstance relation = internalUmlService.fetchInstance(dto.getRelationshipType(), projectId);
        if (relation == null) {
            return Collections.emptyList();
        }

        List<String> metaclassList;
        if (dto.getRelationshipType().equals(UMLDefinitionConstants.Generalization)) {
            metaclassList = Collections.singletonList(sourceInstance.getMetaClass().getId());
        } else {
            ModelInfo info = internalUmlService.fetchSpecificModelInfoValue(projectId, relation);
            if (info != null) {
                metaclassList = queryRelationEndTypeByInfo(dto, projectId, sourceInstance, info);
            } else {
                metaclassList = internalUmlService.queryRelationEndType(projectId, sourceInstance,
                                dto.getRelationshipType(), dto.getDirection())
                        .stream().map(BasicRElement::getId).collect(Collectors.toList());
            }
        }

        return metaclassList;
    }

    public List<String> batchQueryValidateEndTypes(@Valid BatchQueryRelationshipEndDTO dto) {
        final String projectId = dto.getProjectId();
        List<MetaClassInstance> instances = internalUmlService.fetchInstanceByIds(dto.getFirstEndId(), projectId);
        if (CollectionUtils.isEmpty(instances)) {
            return Collections.emptyList();
        }

        MetaClassInstance relation = internalUmlService.fetchInstance(dto.getRelationshipType(), projectId);
        if (relation == null) {
            return Collections.emptyList();
        }
        List<List<String>> metaclassList = Lists.newArrayList();
        for (MetaClassInstance sourceInstance : instances) {
            if (dto.getRelationshipType().equals(UMLDefinitionConstants.Generalization)) {
                return Collections.singletonList(sourceInstance.getMetaClass().getId());
            } else {
                ModelInfo info = internalUmlService.fetchSpecificModelInfoValue(projectId, relation);
                if (info != null) {
                    QueryRelationshipEndDTO queryRelationshipEndDTO = BeanUtil.copyProperties(dto, QueryRelationshipEndDTO.class);
                    metaclassList.add(queryRelationEndTypeByInfo(queryRelationshipEndDTO, projectId, sourceInstance, info));
                } else {
                    metaclassList.add(internalUmlService.queryRelationEndType(projectId, sourceInstance, dto.getRelationshipType(), dto.getDirection()).stream().map(BasicRElement::getId).collect(Collectors.toList()));
                }
            }
        }
        List<String> result = metaclassList.get(0);
        for (int i = 1; i < metaclassList.size(); i++) {
            result = Lists.newArrayList(CollUtil.intersection(result, metaclassList.get(i)));
        }
        return result;
    }

    @NotNull
    private List<String> queryRelationEndTypeByInfo(QueryRelationshipEndDTO dto, String projectId, MetaClassInstance sourceInstance, ModelInfo info) {
        LogUtil.printSerializeOnlineLogs("projectId: {}, sourceInstance: {}, dto: {}", projectId, sourceInstance, dto);
        List<String> metaclassList;
        Set<String> outs;
        ValidateEndInfo validateEnds = customizationModelService.getValidateEndInfo(dto.getProjectId(), info.getValue());
        if (validateEnds == null) {
            metaclassList = internalUmlService.queryRelationEndType(projectId, sourceInstance,
                            dto.getRelationshipType(), dto.getDirection())
                    .stream().map(BasicRElement::getId).collect(Collectors.toList());
        } else {

            if (dto.getRelationshipType().equals(UMLDefinitionConstants.SysML_Block)) {
                validateEnds = managerConfig.getValidateEndInfoConfig().get(ASSOCIATION_BLOCK);
            }
            if (dto.getDirection().equals(IN)) {
                outs = validateEnds.getIncoming();
            } else {
                outs = validateEnds.getOutgoing();
            }

            metaclassList = outs.stream().map(it -> managerConfig.getModelInfoConfig().get(it))
                    .filter(Objects::nonNull)
                    .map(this::getTypeIdByModelInfo)
                    .distinct()
                    .collect(Collectors.toList());
        }
        return metaclassList;
    }


    /**
     * @param displayRelatedElementDto
     * @return com.huawang.mdesign.model.service.vo.AssociateTypeVo
     * @description 根据关系一端的元素，查询此元素拥有的关系类型
     * @author baibf
     * @date 2022/4/29
     */
    @NotNull
    public AssociateTypeVo queryElementRelations(DisplayRelatedElementDTO displayRelatedElementDto) {
        LogUtil.printSerializeOnlineLogs("queryElementRelations -> displayRelatedElementDto: ", displayRelatedElementDto);
        final String projectId = displayRelatedElementDto.getProjectId();
        AssociateTypeVo associateTypeVo = new AssociateTypeVo();
        MetaClassInstance instance = internalUmlService.fetchInstance(displayRelatedElementDto.getInstanceId(), projectId);
        if (instance == null) {
            return associateTypeVo;
        }

        // 获取一端是指定元素的关系
        List<MetaClassInstance> relations = gainIncludeEndRelationShip(displayRelatedElementDto);
        List<ElementType> elementTypes = new ArrayList<>();

        // 获取画布上已经存在的包含关系
        ContainmentRelation containmentRelation =
                getContainmentRelation(displayRelatedElementDto.getExcludeRelationIds(), projectId);
        // 获取包含关系
        batchGetContainment(projectId, Stream.of(instance).collect(Collectors.toList()), elementTypes, containmentRelation);
        List<ElementType> elementTypeList = getElementTypes(projectId, relations, elementTypes);
        associateTypeVo.setAssociates(elementTypeList);
        return associateTypeVo;
    }

    @Getter
    public static class ContainmentRelation {
        private final Set<MetaClassInstance> containmentSources = new HashSet<>();
        private final Set<MetaClassInstance> containmentTargets = new HashSet<>();
    }

    @NotNull
    public AssociateTypeVo batchQueryElementRelations(DisplayRelatedElementBatchDTO dto) {
        LogUtil.printSerializeOnlineLogs("batchQueryElementRelations -> dto: ", dto);
        final String projectId = dto.getProjectId();
        AssociateTypeVo associateTypeVo = new AssociateTypeVo();
        Map<String, MetaClassInstance> fetchInstances = internalUmlService.fetchInstances(dto.getInstanceIds(), projectId);
        if (CollUtil.isEmpty(fetchInstances)) {
            return associateTypeVo;
        }
        //批量获取全部关系
        List<MetaClassInstance> relations = batchGainIncludeEndRelationShip(dto);
        List<ElementType> elementTypes = new ArrayList<>();
        // 获取画布上已经存在的包含关系
        ContainmentRelation containmentRelation = getContainmentRelation(dto.getExcludeRelationIds(), projectId);
        // 获取包含关系
        batchGetContainment(projectId, Lists.newArrayList(fetchInstances.values()), elementTypes, containmentRelation);
        List<ElementType> elementTypeList = getElementTypes(projectId, relations, elementTypes);
        associateTypeVo.setAssociates(elementTypeList);
        return associateTypeVo;
    }

    @NotNull
    private List<ElementType> getElementTypes(String projectId, List<MetaClassInstance> relations, List<ElementType> elementTypes) {
        if (CollectionUtils.isNotEmpty(relations)) {
            relations.forEach(relation -> {
                final ElementType elementType = addElementType(projectId, relation);
                if (elementType != null) {
                    elementTypes.add(elementType);
                }
            });
        }
        // 按关系类型去重
        Set<ElementType> relationTypeSet = new TreeSet<>(Comparator.comparing(ElementType::getIcon));
        relationTypeSet.addAll(elementTypes);
        return new ArrayList<>(relationTypeSet);
    }

    @NotNull
    private ContainmentRelation getContainmentRelation(final List<String> excludeRelationIds,
                                                       String projectId) {
        ContainmentRelation containmentRelation = new ContainmentRelation();
        if (CollectionUtils.isNotEmpty(excludeRelationIds)) {
            List<String> containmentList = excludeRelationIds.stream().filter(it ->
                    it.startsWith("Containment##")).collect(Collectors.toList());

            List<String> containmentEnds = containmentList.stream().flatMap(containment -> {
                String[] containmentSplit = containment.split("##");
                return Stream.of(containmentSplit[1], containmentSplit[2]);
            }).collect(Collectors.toList());
            Map<String, MetaClassInstance> instanceMap = internalUmlService.fetchInstances(containmentEnds, projectId);

            containmentList.forEach(containment -> {
                String[] containmentSplit = containment.split("##");
                containmentRelation.getContainmentSources().add(instanceMap.get(containmentSplit[1]));
                containmentRelation.getContainmentTargets().add(instanceMap.get(containmentSplit[2]));
            });
        }
        return containmentRelation;
    }

    @Nullable
    private ElementType addElementType(String projectId, MetaClassInstance relation) {
        ModelInfo info = managerConfig.getModelInfoConfig().get(elementModelInfoService.querySysModelInfoKey(projectId, relation));
        if (info == null) {
            return null;
        }

        ElementType elementType = new ElementType();
        String icon = info.getIcon();
        String name = info.getCnName();
        elementType.setIcon(icon);
        elementType.setName(name);
        elementType.setValue(info.getEnName());
        return elementType;
    }

    private void batchGetContainment(@NotNull String projectId,
                                     @NotNull List<MetaClassInstance> instances,
                                     @NotNull List<ElementType> elementTypes,
                                     @NotNull ContainmentRelation containmentRelation) {
        LogUtil.printSerializeOnlineLogs("projectId: {}, instances: {}, elementTypes: {}, containmentRelation: {}", projectId, instances, elementTypes, containmentRelation);
        // 得到画布上不存在的包含关系，并添加包含关系
        QueryPropertyForm instanceForm = internalUmlService.getQueryPropertyForm(instances,
                Lists.newArrayList(UMLDefinitionConstants.NamedElement_namespace, UMLDefinitionConstants.Element_ownedElement));
        MofResultContainer instanceResult = internalUmlService.queryProperty(projectId, instanceForm);
        final Set<MetaClassInstance> containmentSources = containmentRelation.getContainmentSources();
        final Set<MetaClassInstance> containmentTargets = containmentRelation.getContainmentTargets();
        for (MetaClassInstance instance : instances) {
            MetaClassInstance namespaceInstance = instanceResult.queryInstanceProperty(
                    instance.getId(), UMLDefinitionConstants.NamedElement_namespace);
            if (containmentSources.contains(namespaceInstance) || containmentTargets.contains(namespaceInstance)) {
                namespaceInstance = null;
            }
            List<MetaClassInstance> ownedElementInstances = instanceResult.queryListInstanceProperty(
                    instance.getId(), UMLDefinitionConstants.Element_ownedElement);
            ownedElementInstances.removeIf(it -> containmentSources.contains(it) || containmentTargets.contains(it));
            boolean flag = false;
            Set<String> instanceGivenTypes = internalUmlService.getGivenTypes(projectId, instance);
            if (CollectionUtils.isNotEmpty(ownedElementInstances) && !instanceGivenTypes.contains(UMLDefinitionConstants.Stereotype)) {
                QueryPropertyForm ownedElementForm = internalUmlService.getQueryPropertyForm(
                        ownedElementInstances, UMLDefinitionConstants.NamedElement_namespace);
                MofResultContainer ownedElementResult = internalUmlService.queryProperty(projectId, ownedElementForm);
                for (MetaClassInstance ownedElement : ownedElementInstances) {
                    MetaClassInstance ownedElementNamespaceInstance = ownedElementResult.queryInstanceProperty(
                            ownedElement.getId(), UMLDefinitionConstants.NamedElement_namespace);
                    Set<String> ownedElementGivenTypes = internalUmlService.getGivenTypes(projectId, ownedElement);
                    flag = Objects.nonNull(ownedElementNamespaceInstance)
                            && (ownedElementGivenTypes.contains(UMLDefinitionConstants.Package)
                            || ownedElementGivenTypes.contains(UMLDefinitionConstants.Classifier)
                            && !ownedElementGivenTypes.contains(UMLDefinitionConstants.Association))
                            && ownedElementNamespaceInstance.getId().equals(instance.getId());
                }
            }

            if (((instanceGivenTypes.contains(UMLDefinitionConstants.Package)
                    || instanceGivenTypes.contains(UMLDefinitionConstants.Classifier))
                    && Objects.nonNull(namespaceInstance)) || flag) {
                ElementType containment = new ElementType();
                containment.setIcon("statics/images/sysml/Containment.svg");
                containment.setName("包含");
                containment.setValue(ModelConstantURI.CONTAINMENT);
                elementTypes.add(containment);
                return;
            }
        }


    }

    /**
     * @param displayRelatedElementDto
     * @return com.huawang.mdesign.model.service.vo.RelatedElementVo
     * @description 根据关系一端的元素，关系的方向、类型，元素的范围，查询此元素拥有的与类型匹配的所有关系和关系另一端的元素
     * @author baibf
     * @date 2022/4/29
     */
    @NotNull
    public RelatedElementVo queryRelatedElements(DisplayRelatedElementDTO displayRelatedElementDto) {
        LogUtil.printSerializeOnlineLogs("queryRelatedElements -> displayRelatedElementDto: ", displayRelatedElementDto);
        final String projectId = displayRelatedElementDto.getProjectId();

        RelatedElementVo relatedElementVo = new RelatedElementVo();

        MetaClassInstance instance = internalUmlService.fetchInstance(displayRelatedElementDto.getInstanceId(),
                projectId);
        if (Objects.isNull(instance)) {
            return relatedElementVo;
        }
        // 获取一端是指定元素的关系
        List<MetaClassInstance> relations = gainIncludeEndRelationShip(displayRelatedElementDto);

        // 筛选符合类型的关系
        List<MetaClassInstance> kindRelations = new ArrayList<>();
        relations.forEach(relation -> {
            ModelInfo info = managerConfig.getModelInfoConfig().get(elementModelInfoService.querySysModelInfoKey(projectId, relation));
            if (displayRelatedElementDto.getRelationTypes().contains(info.getEnName())) {
                kindRelations.add(relation);
            }
        });
        List<DirectionRelatedElement> incomingRelatedElements = new ArrayList<>();
        List<DirectionRelatedElement> outgoingRelatedElements = new ArrayList<>();
        // 有对应模型的关系处理
        if (CollectionUtils.isNotEmpty(kindRelations)) {
            gainRelationElement(displayRelatedElementDto, kindRelations, incomingRelatedElements, outgoingRelatedElements);
        }
        // 关系是Containment，由于Containment没有对应模型，此处特殊处理
        if (displayRelatedElementDto.getRelationTypes().contains(ModelConstantURI.CONTAINMENT)) {
            gainContainmentElement(displayRelatedElementDto, instance, incomingRelatedElements,
                    outgoingRelatedElements);
        }

        // 处理范围
        if (PACKAGE.equals(displayRelatedElementDto.getScope())) {
            MetaClassInstance packageInstance = elementOfPackage(projectId, instance);
            // 获取包下所有的模型
            List<MetaClassInstance> allChildInstances = internalUmlService.getChildren(packageInstance,
                    false, projectId);
            if (CollectionUtils.isNotEmpty(allChildInstances)) {
                List<String> allChildInstanceIds = allChildInstances.stream().map(MetaClassInstance::getId)
                        .collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(incomingRelatedElements)) {
                    incomingRelatedElements.removeIf(incomingRelatedElement ->
                            !allChildInstanceIds.contains(incomingRelatedElement.getRelatedElementId()));
                }
                if (CollectionUtils.isNotEmpty(outgoingRelatedElements)) {
                    outgoingRelatedElements.removeIf(outgoingRelatedElement ->
                            !allChildInstanceIds.contains(outgoingRelatedElement.getRelatedElementId()));
                }
            }
        }

        incomingRelatedElements.removeIf(it -> StringUtils.equals(it.getRelatedElementId(),
                UMLDefinitionConstants.CUSTOMIZATION_CONSTRAINT_CONFIG));
        relatedElementVo.setInstanceId(displayRelatedElementDto.getInstanceId());
        relatedElementVo.setIncomingRelatedElements(incomingRelatedElements);
        relatedElementVo.setOutgoingRelatedElements(outgoingRelatedElements);

        return relatedElementVo;
    }

    @NotNull
    public List<DirectionRelatedElement> batchQueryRelatedElements(DisplayRelatedElementBatchDTO dto) {
        LogUtil.printSerializeOnlineLogs("queryRelatedElements -> displayRelatedElementDto: ", dto);
        final String projectId = dto.getProjectId();
        Map<String, MetaClassInstance> fetchInstances = internalUmlService.fetchInstances(dto.getInstanceIds(), projectId);
        if (CollUtil.isEmpty(fetchInstances)) {
            return Lists.newArrayList();
        }
        //批量获取全部关系
        List<MetaClassInstance> relations = batchGainIncludeEndRelationShip(dto);
        // 筛选符合类型的关系
        List<MetaClassInstance> kindRelations = new ArrayList<>();
        relations.forEach(relation -> {
            ModelInfo info = managerConfig.getModelInfoConfig().get(elementModelInfoService.querySysModelInfoKey(projectId, relation));
            if (info != null && dto.getRelationTypes().contains(info.getEnName())) {
                kindRelations.add(relation);
            }
        });
        List<DirectionRelatedElement> relatedElements = Lists.newArrayList();
        // 有对应模型的关系处理
        if (CollectionUtils.isNotEmpty(kindRelations)) {
            batchGainRelationElement(dto, kindRelations, relatedElements);
        }
        // 关系是Containment，由于Containment没有对应模型，此处特殊处理
        if (dto.getRelationTypes().contains(ModelConstantURI.CONTAINMENT)) {
            gainContainmentElement(dto, Lists.newArrayList(fetchInstances.values()), relatedElements);
        }

        // 处理范围
        if (PACKAGE.equals(dto.getScope())) {
            Set<MetaClassInstance> packageInstances = batchGetPackage(projectId, Lists.newArrayList(fetchInstances.values()));
            for (MetaClassInstance packageInstance : packageInstances) {
                // 获取包下所有的模型
                List<MetaClassInstance> allChildInstances = internalUmlService.getChildren(packageInstance,
                        false, projectId);
                if (CollectionUtils.isNotEmpty(allChildInstances)) {
                    List<String> allChildInstanceIds = allChildInstances.stream().map(MetaClassInstance::getId)
                            .collect(Collectors.toList());
                    relatedElements.removeIf(incomingRelatedElement ->
                            !allChildInstanceIds.contains(incomingRelatedElement.getRelatedElementId()));
                }
            }
        }
        relatedElements.removeIf(it -> StringUtils.equals(it.getRelatedElementId(), UMLDefinitionConstants.CUSTOMIZATION_CONSTRAINT_CONFIG));
        return relatedElements;
    }

    private Set<MetaClassInstance> batchGetPackage(String projectId, List<MetaClassInstance> metaClassInstances) {
        Set<MetaClassInstance> packageInstances = new HashSet<>();
        List<MetaClassInstance> ownerInstances = internalUmlService.queryProperty(projectId, metaClassInstances, UMLDefinitionConstants.Element_owner);
        for (MetaClassInstance ownerInstance : ownerInstances) {
            if (internalUmlService.checkInstanceType(projectId, ownerInstance, UMLDefinitionConstants.Package)) {
                packageInstances.add(ownerInstance);
            } else {
                packageInstances.add(elementOfPackage(projectId, ownerInstance));
            }
        }
        return packageInstances;
    }

    /**
     *
     * @param displayRelatedElementDto
     * @param level 层级这个层级是递归的层级，默认为1，用户输入的层级
     * @return
     */
    @NotNull
    public List<RelatedElementVo> queryAllRelatedElements(DisplayRelatedElementDTO displayRelatedElementDto, Integer level){
        RelatedElementVo relatedElementVo =  queryRelatedElements(displayRelatedElementDto);
        Integer elementLevel = relatedElementVo.getElementLevel();
        return queryRelatedChildrenElements(displayRelatedElementDto,
                relatedElementVo.getIncomingRelatedElements(),
                relatedElementVo.getOutgoingRelatedElements(),
                Stream.of(relatedElementVo).collect(Collectors.toList()), ++elementLevel, --level);
    }

    @NotNull
    public BatchRelatedElementVo batchQueryRelationshipElements(DisplayRelatedElementBatchDTO dto){
        BatchRelatedElementVo result = new BatchRelatedElementVo();
        List<DirectionRelatedElement> relatedElements= Lists.newArrayList();
        recursionGetRelated(batchQueryRelatedElements(dto),relatedElements, dto);
        result.setRelatedElements(relatedElements.stream().distinct().collect(Collectors.toList()));
        return result;
    }

    private void recursionGetRelated(List<DirectionRelatedElement> directionRelatedElements, List<DirectionRelatedElement> relatedElements, DisplayRelatedElementBatchDTO dto) {
        List<String> instanceIds = dto.getInstanceIds();
        if (CollUtil.isEmpty(directionRelatedElements)) {
            return;
        }
        relatedElements.addAll(directionRelatedElements);
        int level = dto.getLevel();
        if (level == 1) {
            return;
        }
        //当前查询出来的关系ID存在画布上则不继续往下面查询
        directionRelatedElements.removeIf(next -> CollUtil.contains(dto.getExcludeRelationIds(), next.getRelationShipId()));
        //拿到上一层获取到的所有关系ID 进行下一波查询
        List<String> queryInstanceIds = Lists.newArrayList();
        queryInstanceIds.addAll(directionRelatedElements.stream().map(DirectionRelatedElement::getRelatedElementId).filter(e -> !instanceIds.contains(e)).collect(Collectors.toList()));
        queryInstanceIds.addAll(directionRelatedElements.stream().map(DirectionRelatedElement::getSourceElementId).filter(e -> !instanceIds.contains(e)).collect(Collectors.toList()));

        dto.setInstanceIds(queryInstanceIds);
        dto.setLevel(level-1);
        recursionGetRelated(batchQueryRelatedElements(dto), relatedElements, dto);
    }


    /**
     * 根据关系一端的元素，关系的方向、类型，元素的范围，查询此元素拥有的与类型匹配的所有关系和关系另一端的子元素
     * @param displayRelatedElementDto
     * @param incomingRelatedElements
     * @param outgoingRelatedElements
     * @param relatedElementVos
     * @param elementLevel 告诉前端哪些元素是哪一层的
     * @param level 层级这个层级是递归的层级，默认为1，用户输入的层级
     * @return com.huawang.mdesign.model.service.vo.RelatedElementVo
     * @author lvh
     * @date 2024/12/19
     */
    private List<RelatedElementVo> queryRelatedChildrenElements(
            @NotNull DisplayRelatedElementDTO displayRelatedElementDto,
            @NotNull List<DirectionRelatedElement> incomingRelatedElements,
            @NotNull List<DirectionRelatedElement> outgoingRelatedElements,
            @NotNull List<RelatedElementVo> relatedElementVos,
            @NotNull Integer elementLevel,
            @NotNull Integer level) {
        if(incomingRelatedElements.isEmpty() && outgoingRelatedElements.isEmpty()) {
            return relatedElementVos;
        }

        if(level <= 0){
            return relatedElementVos;
        }
        //保存当前实例下一级的关系,用于下一层递归
        List<DirectionRelatedElement> incomingRelatedChildrenElements = Lists.newArrayList();
        List<DirectionRelatedElement> outgoingRelatedChildrenElements = Lists.newArrayList();
        // 获取已经查询出的模型map key为模型id
        processRelatedElements(elementLevel, displayRelatedElementDto, incomingRelatedElements, relatedElementVos, incomingRelatedChildrenElements, outgoingRelatedChildrenElements);
        processRelatedElements(elementLevel, displayRelatedElementDto, outgoingRelatedElements, relatedElementVos, incomingRelatedChildrenElements, outgoingRelatedChildrenElements);
        //向下一层层递归
        queryRelatedChildrenElements(displayRelatedElementDto, incomingRelatedChildrenElements, outgoingRelatedChildrenElements, relatedElementVos, ++elementLevel, --level);
        return relatedElementVos;
    }

    /**
     * 对 outgoingRelatedElements 或 incomingRelatedElements 的List 进行处理
     * @param displayRelatedElementDto
     * @param relatedElements
     * @param relatedElementVos
     * @param incomingRelatedChildrenElements
     * @param outgoingRelatedChildrenElements
     * @return void
     * @author lvh
     * @date 2024/12/19
     */
    private void processRelatedElements(
            @NotNull Integer elementLevel,
            @NotNull DisplayRelatedElementDTO displayRelatedElementDto,
            @NotNull List<DirectionRelatedElement> relatedElements,
            @NotNull List<RelatedElementVo> relatedElementVos,
            @NotNull List<DirectionRelatedElement> incomingRelatedChildrenElements,
            @NotNull List<DirectionRelatedElement> outgoingRelatedChildrenElements) {
        // 获取已经查询出的模型map key为模型id
        Map<String, RelatedElementVo> relatedElementVoMap = relatedElementVos.stream()
                .collect(Collectors.toMap(RelatedElementVo::getInstanceId, vo -> vo));

        for (DirectionRelatedElement relatedElement : relatedElements) {
            String instanceId = relatedElement.getRelatedElementId();
            RelatedElementVo relatedElementVo = relatedElementVoMap.get(instanceId);
            if(Objects.isNull(relatedElementVo)) {
                displayRelatedElementDto.setInstanceId(instanceId);
                RelatedElementVo relatedChildrenElementVo = queryRelatedElements(displayRelatedElementDto);
                relatedChildrenElementVo.setElementLevel(elementLevel);
                relatedElementVos.add(relatedChildrenElementVo);
                // 获取 getIncomingRelatedElements 中独有的元素
                List<DirectionRelatedElement> uniqueIncomingElements = getUniqueElements(relatedChildrenElementVo.getIncomingRelatedElements(), relatedElementVoMap);
                incomingRelatedChildrenElements.addAll(uniqueIncomingElements);
                // 获取 getOutgoingRelatedElements 中独有的元素
                List<DirectionRelatedElement> uniqueOutgoingElements = getUniqueElements(relatedChildrenElementVo.getOutgoingRelatedElements(), relatedElementVoMap);
                outgoingRelatedChildrenElements.addAll(uniqueOutgoingElements);
            }
        }
    }


    /**
     * 获取独有元素 先把elementVoList转成map，再根据map的key选出relatedElementVoMap中不存在key的元素,再getValue,再转成list
     * @param elementVoList
     * @param relatedElementVoMap
     * @return com.huawang.mdesign.model.service.vo.DirectionRelatedElement
     * @author lvh
     * @date 2024/12/19
     */
    private List<DirectionRelatedElement> getUniqueElements(
            @NotNull List<DirectionRelatedElement> elementVoList,
            @NotNull Map<String, RelatedElementVo> relatedElementVoMap) {
        return elementVoList.stream()
                .collect(Collectors.toMap(DirectionRelatedElement::getRelatedElementId, vo -> vo))
                .entrySet().stream()
                .filter(entry -> !relatedElementVoMap.containsKey(entry.getKey()))
                .map(Map.Entry::getValue)
                .collect(Collectors.toList());
    }


    /**
     * @param displayRelatedElementDto
     * @param relations                关系
     * @param incomingRelatedElements  入方向的元素
     * @param outgoingRelatedElements  出方向的元素
     * @return void
     * @description 根据有对应模型关系一端和方向，获取关系另一端元素
     * @author baibf
     * @date: 2022/5/18
     */
    private void gainRelationElement(@NotNull DisplayRelatedElementDTO displayRelatedElementDto,
                                     @NotNull List<MetaClassInstance> relations,
                                     @NotNull List<DirectionRelatedElement> incomingRelatedElements,
                                     @NotNull List<DirectionRelatedElement> outgoingRelatedElements) {
        LogUtil.printSerializeOnlineLogs("displayRelatedElementDto: {}, relations: {}, incomingRelatedElements: {}, outgoingRelatedElements: {}",
                displayRelatedElementDto, relations, incomingRelatedElements, outgoingRelatedElements);
        final String projectId = displayRelatedElementDto.getProjectId();

        List<MetaClassInstance> associations = relations.stream().filter(it ->
                internalUmlService.checkInstanceType(projectId, it, UMLDefinitionConstants.Association)).collect(Collectors.toList());
        List<MetaClassInstance> directedRelationships = relations.stream().filter(it ->
                internalUmlService.checkInstanceType(projectId, it, UMLDefinitionConstants.DirectedRelationship)).collect(Collectors.toList());
        List<MetaClassInstance> connectors = relations.stream().filter(it ->
                internalUmlService.checkInstanceType(projectId, it, UMLDefinitionConstants.Connector)).collect(Collectors.toList());

        // 关系是Association进行填充
        relationIsAssociation(displayRelatedElementDto, associations, incomingRelatedElements, outgoingRelatedElements);
        // 关系是DirectedRelationship进行填充
        relationIsDirectedRelationship(displayRelatedElementDto, directedRelationships, incomingRelatedElements, outgoingRelatedElements);
        // 关系是Connector进行填充
        relationIsConnector(displayRelatedElementDto, connectors, incomingRelatedElements, outgoingRelatedElements);
    }

    private void batchGainRelationElement(@NotNull DisplayRelatedElementBatchDTO dto,
                                     @NotNull List<MetaClassInstance> relations,
                                     @NotNull List<DirectionRelatedElement> relatedElements) {
        final String projectId = dto.getProjectId();

        List<MetaClassInstance> associations = relations.stream().filter(it ->
                internalUmlService.checkInstanceType(projectId, it, UMLDefinitionConstants.Association)).collect(Collectors.toList());
        List<MetaClassInstance> directedRelationships = relations.stream().filter(it ->
                internalUmlService.checkInstanceType(projectId, it, UMLDefinitionConstants.DirectedRelationship)).collect(Collectors.toList());
        List<MetaClassInstance> connectors = relations.stream().filter(it ->
                internalUmlService.checkInstanceType(projectId, it, UMLDefinitionConstants.Connector)).collect(Collectors.toList());

//        // 关系是Association进行填充
        relationIsAssociation(dto, associations, relatedElements);
        // 关系是DirectedRelationship进行填充
        relationIsDirectedRelationship(dto, directedRelationships, relatedElements);
//        // 关系是Connector进行填充
        relationIsConnector(dto, connectors, relatedElements);
    }

    private void relationIsAssociation(@NotNull DisplayRelatedElementDTO displayRelatedElementDto,
                                       @NotNull List<MetaClassInstance> associations,
                                       @NotNull List<DirectionRelatedElement> incomingRelatedElements,
                                       @NotNull List<DirectionRelatedElement> outgoingRelatedElements) {
        LogUtil.printSerializeOnlineLogs("displayRelatedElementDto: {}, associations: {}, incomingRelatedElements: {}, outgoingRelatedElements: {}",
                displayRelatedElementDto, associations, incomingRelatedElements, outgoingRelatedElements);
        if (CollectionUtils.isEmpty(associations)) {
            return;
        }

        final String projectId = displayRelatedElementDto.getProjectId();
        QueryPropertyForm associationsForm = internalUmlService.getQueryPropertyForm(associations,
                Stream.of(UMLDefinitionConstants.Relationship_relatedElement,
                        UMLDefinitionConstants.Association_ownedEnd).collect(Collectors.toList()));
        MofResultContainer associationsResult = internalUmlService.queryProperty(projectId, associationsForm);

        associations.forEach(association -> {
            DirectionRelatedElement directionRelatedElement = new DirectionRelatedElement();
            List<MetaClassInstance> elements = associationsResult.queryListInstanceProperty(
                    association.getId(), UMLDefinitionConstants.Relationship_relatedElement);
            if (CollectionUtils.isEmpty(elements)) {
                return;
            }
            // 如果association的ownedEnd有值，则视为有方向；
            if (CollectionUtils.isNotEmpty(associationsResult.queryListInstanceProperty(
                    association.getId(), UMLDefinitionConstants.Association_ownedEnd))) {
                // 处理方向
                directedAssociationHandler(displayRelatedElementDto, association, directionRelatedElement, incomingRelatedElements, outgoingRelatedElements, elements);
            } else if (elements.size() > 1 && elements.get(1).getId().equals(displayRelatedElementDto.getInstanceId())) {
                // 此时Association没有方向，
                directionRelatedElement.setRelatedElementId(elements.get(0).getId());
                directionRelatedElement.setRelationShipId(association.getId());
                outgoingRelatedElements.add(directionRelatedElement);
            } else {
                if (elements.size() > 1) {
                    directionRelatedElement.setRelatedElementId(elements.get(1).getId());
                    directionRelatedElement.setRelationShipId(association.getId());
                    incomingRelatedElements.add(directionRelatedElement);
                }
            }
        });
    }

    private void relationIsAssociation(@NotNull DisplayRelatedElementBatchDTO dto,
                                       @NotNull List<MetaClassInstance> associations,
                                       @NotNull List<DirectionRelatedElement> relatedElements) {
        if (CollectionUtils.isEmpty(associations)) {
            return;
        }

        final String projectId = dto.getProjectId();
        QueryPropertyForm associationsForm = internalUmlService.getQueryPropertyForm(associations,
                Lists.newArrayList(UMLDefinitionConstants.Relationship_relatedElement, UMLDefinitionConstants.Association_ownedEnd));
        MofResultContainer associationsResult = internalUmlService.queryProperty(projectId, associationsForm);

        associations.forEach(association -> {
            DirectionRelatedElement directionRelatedElement = new DirectionRelatedElement();
            List<MetaClassInstance> elements = associationsResult.queryListInstanceProperty(association.getId(), UMLDefinitionConstants.Relationship_relatedElement);
            if (elements == null) {
                return;
            }

            if (elements.size() == 1) {
                directionRelatedElement.setRelatedElementId(elements.get(0).getId());
                directionRelatedElement.setRelationShipId(association.getId());
                directionRelatedElement.setSourceElementId(elements.get(0).getId());
                relatedElements.add(directionRelatedElement);
            } else {
                directionRelatedElement.setRelatedElementId(elements.get(0).getId());
                directionRelatedElement.setRelationShipId(association.getId());
                directionRelatedElement.setSourceElementId(elements.get(1).getId());
                relatedElements.add(directionRelatedElement);
            }

        });
    }

    private static void directedAssociationHandler(DisplayRelatedElementDTO displayRelatedElementDto,
                                                   MetaClassInstance relation,
                                                   DirectionRelatedElement directionRelatedElement,
                                                   List<DirectionRelatedElement> incomingRelatedElements,
                                                   List<DirectionRelatedElement> outgoingRelatedElements,
                                                   List<MetaClassInstance> elements) {
        LogUtil.printSerializeOnlineLogs("displayRelatedElementDto: {}, relation: {}, directionRelatedElement: {}, incomingRelatedElements: {}, outgoingRelatedElements: {}, elements: {}",
                displayRelatedElementDto, relation, directionRelatedElement, incomingRelatedElements, outgoingRelatedElements, elements);
        final String relationDirection = displayRelatedElementDto.getRelationDirection();
        if (INCOMING.equals(relationDirection) && elements.size() > 1
                && Objects.nonNull(elements.get(1))
                && !elements.get(1).getId().equals(displayRelatedElementDto.getInstanceId())) {
            directionRelatedElement.setRelatedElementId(elements.get(1).getId());
            directionRelatedElement.setRelationShipId(relation.getId());
            incomingRelatedElements.add(directionRelatedElement);
            return;
        }

        if (OUTGOING.equals(relationDirection)
                && Objects.nonNull(elements.get(0))
                && !elements.get(0).getId().equals(displayRelatedElementDto.getInstanceId())) {
            directionRelatedElement.setRelatedElementId(elements.get(0).getId());
            directionRelatedElement.setRelationShipId(relation.getId());
            outgoingRelatedElements.add(directionRelatedElement);
            return;
        }

        if (BOTH.equals(relationDirection)) {
            if (elements.size() > 1) {
                if (elements.get(1).getId().equals(displayRelatedElementDto.getInstanceId())) {
                    directionRelatedElement.setRelatedElementId(elements.get(0).getId());
                    directionRelatedElement.setRelationShipId(relation.getId());
                    outgoingRelatedElements.add(directionRelatedElement);
                } else {
                    directionRelatedElement.setRelatedElementId(elements.get(1).getId());
                    directionRelatedElement.setRelationShipId(relation.getId());
                    incomingRelatedElements.add(directionRelatedElement);
                }
            } else {
                directionRelatedElement.setRelatedElementId(elements.get(0).getId());
                directionRelatedElement.setRelationShipId(relation.getId());
                outgoingRelatedElements.add(directionRelatedElement);
            }
        }
    }

    @Data
    public static class DisplayConnectorEdge {
        private MetaClassInstance firstRoleInstance = null;
        private MetaClassInstance secondRoleInstance = null;
    }

    private void relationIsConnector(@NotNull DisplayRelatedElementBatchDTO dto,
                                     @NotNull List<MetaClassInstance> connectors,
                                     @NotNull List<DirectionRelatedElement> relatedElements) {
        if (CollectionUtils.isEmpty(connectors)) {
            return;
        }

        final String projectId = dto.getProjectId();
        QueryPropertyForm connectorsForm = internalUmlService.getQueryPropertyForm(connectors, UMLDefinitionConstants.Connector_end);
        MofResultContainer connectorsResult = internalUmlService.queryProperty(projectId, connectorsForm);
        connectors.forEach(connector -> {
            DirectionRelatedElement directionRelatedElement = new DirectionRelatedElement();
            DisplayConnectorEdge displayConnectorEdge = getDirectionRelatedElement(projectId, connectorsResult, connector);
            directionRelatedElement.setRelationShipId(connector.getId());
            if (displayConnectorEdge.getFirstRoleInstance() != null) {
                directionRelatedElement.setSourceElementId(displayConnectorEdge.getFirstRoleInstance().getId());
            }

            if (displayConnectorEdge.getSecondRoleInstance() != null) {
                directionRelatedElement.setRelatedElementId(displayConnectorEdge.getSecondRoleInstance().getId());
            }
            relatedElements.add(directionRelatedElement);
        });
    }

    @NotNull
    private DisplayConnectorEdge getDirectionRelatedElement(String projectId, MofResultContainer connectorsResult, MetaClassInstance connector) {
        List<MetaClassInstance> endInstances = connectorsResult.queryListInstanceProperty(connector.getId(), UMLDefinitionConstants.Connector_end);
        DisplayConnectorEdge displayConnectorEdge = new DisplayConnectorEdge();
        if (CollectionUtils.isNotEmpty(endInstances) && endInstances.size() > 1) {
            QueryPropertyForm rolesForm = internalUmlService.getQueryPropertyForm(endInstances, UMLDefinitionConstants.ConnectorEnd_role);
            MofResultContainer rolesResult = internalUmlService.queryProperty(projectId, rolesForm);
            displayConnectorEdge.setFirstRoleInstance(rolesResult.queryInstanceProperty(endInstances.get(0).getId(),
                    UMLDefinitionConstants.ConnectorEnd_role));
            displayConnectorEdge.setSecondRoleInstance(rolesResult.queryInstanceProperty(endInstances.get(1).getId(),
                    UMLDefinitionConstants.ConnectorEnd_role));
        }

        return displayConnectorEdge;
    }

    private void relationIsConnector(@NotNull DisplayRelatedElementDTO displayRelatedElementDto,
                                     @NotNull List<MetaClassInstance> connectors,
                                     @NotNull List<DirectionRelatedElement> incomingRelatedElements,
                                     @NotNull List<DirectionRelatedElement> outgoingRelatedElements) {
        LogUtil.printSerializeOnlineLogs("displayRelatedElementDto: {}, connectors: {}, incomingRelatedElements: {}, outgoingRelatedElements: {}",
                displayRelatedElementDto, connectors, incomingRelatedElements, outgoingRelatedElements);
        if (CollectionUtils.isEmpty(connectors)) {
            return;
        }

        final String projectId = displayRelatedElementDto.getProjectId();
        QueryPropertyForm connectorsForm = internalUmlService.getQueryPropertyForm(connectors, UMLDefinitionConstants.Connector_end);
        MofResultContainer connectorsResult = internalUmlService.queryProperty(projectId, connectorsForm);

        connectors.forEach(connector -> {
            DirectionRelatedElement directionRelatedElement = new DirectionRelatedElement();
            DisplayConnectorEdge displayConnectorEdge = getDirectionRelatedElement(projectId, connectorsResult, connector);
            final MetaClassInstance firstRoleInstance = displayConnectorEdge.getFirstRoleInstance();
            final MetaClassInstance secondRoleInstance = displayConnectorEdge.getSecondRoleInstance();
            if (INCOMING.equals(displayRelatedElementDto.getRelationDirection())
                    && Objects.nonNull(firstRoleInstance)
                    && !firstRoleInstance.getId().equals(displayRelatedElementDto.getInstanceId())) {
                directionRelatedElement.setRelatedElementId(firstRoleInstance.getId());
                directionRelatedElement.setRelationShipId(connector.getId());
                incomingRelatedElements.add(directionRelatedElement);
                return;
            }

            if (OUTGOING.equals(displayRelatedElementDto.getRelationDirection())
                    && Objects.nonNull(secondRoleInstance)
                    && !secondRoleInstance.getId().equals(displayRelatedElementDto.getInstanceId())) {
                directionRelatedElement.setRelatedElementId(secondRoleInstance.getId());
                directionRelatedElement.setRelationShipId(connector.getId());
                outgoingRelatedElements.add(directionRelatedElement);
                return;
            }

            if (BOTH.equals(displayRelatedElementDto.getRelationDirection())) {
                if (Objects.nonNull(firstRoleInstance) && Objects.nonNull(secondRoleInstance)
                        && firstRoleInstance.getId().equals(displayRelatedElementDto.getInstanceId())) {
                    directionRelatedElement.setRelatedElementId(secondRoleInstance.getId());
                    directionRelatedElement.setRelationShipId(connector.getId());
                    outgoingRelatedElements.add(directionRelatedElement);
                    return;
                }

                if (Objects.nonNull(firstRoleInstance) && Objects.nonNull(secondRoleInstance)
                        && secondRoleInstance.getId().equals(displayRelatedElementDto.getInstanceId())) {
                    directionRelatedElement.setRelatedElementId(firstRoleInstance.getId());
                    directionRelatedElement.setRelationShipId(connector.getId());
                    incomingRelatedElements.add(directionRelatedElement);
                }
            }
        });
    }

    private void relationIsDirectedRelationship(@NotNull DisplayRelatedElementDTO displayRelatedElementDto,
                                                @NotNull List<MetaClassInstance> directedRelationships,
                                                @NotNull List<DirectionRelatedElement> incomingRelatedElements,
                                                @NotNull List<DirectionRelatedElement> outgoingRelatedElements) {
        LogUtil.printSerializeOnlineLogs("displayRelatedElementDto: {}, directedRelationships: {}, incomingRelatedElements: outgoingRelatedElements: {}",
                displayRelatedElementDto, directedRelationships, incomingRelatedElements, outgoingRelatedElements);
        if (CollectionUtils.isEmpty(directedRelationships)) {
            return;
        }

        final String projectId = displayRelatedElementDto.getProjectId();
        QueryPropertyForm directedRelationshipsForm = internalUmlService.getQueryPropertyForm(
                directedRelationships,
                Stream.of(UMLDefinitionConstants.DirectedRelationship_source, UMLDefinitionConstants.DirectedRelationship_target)
                        .collect(Collectors.toList()));
        MofResultContainer directedRelationshipsResult = internalUmlService.queryProperty(projectId, directedRelationshipsForm);

        directedRelationships.forEach(directedRelationship -> {
            DirectionRelatedElement directionRelatedElement = new DirectionRelatedElement();
            MetaClassInstance sourceInstance = directedRelationshipsResult.queryListInstanceProperty(
                    directedRelationship.getId(), UMLDefinitionConstants.DirectedRelationship_source).get(0);
            MetaClassInstance targetInstance = directedRelationshipsResult.queryListInstanceProperty(
                    directedRelationship.getId(), UMLDefinitionConstants.DirectedRelationship_target).get(0);
            if (Objects.nonNull(sourceInstance) && INCOMING.equals(displayRelatedElementDto.getRelationDirection())
                    && !sourceInstance.getId().equals(displayRelatedElementDto.getInstanceId())) {
                directionRelatedElement.setRelatedElementId(sourceInstance.getId());
                directionRelatedElement.setRelationShipId(directedRelationship.getId());
                incomingRelatedElements.add(directionRelatedElement);
                return;
            }

            if (Objects.nonNull(targetInstance) && OUTGOING.equals(displayRelatedElementDto.getRelationDirection())
                    && !targetInstance.getId().equals(displayRelatedElementDto.getInstanceId())) {
                directionRelatedElement.setRelatedElementId(targetInstance.getId());
                directionRelatedElement.setRelationShipId(directedRelationship.getId());
                outgoingRelatedElements.add(directionRelatedElement);
                return;
            }

            if (BOTH.equals(displayRelatedElementDto.getRelationDirection())) {
                if (Objects.nonNull(sourceInstance) && Objects.nonNull(targetInstance)
                        && sourceInstance.getId().equals(displayRelatedElementDto.getInstanceId())) {
                    directionRelatedElement.setRelatedElementId(targetInstance.getId());
                    directionRelatedElement.setRelationShipId(directedRelationship.getId());
                    outgoingRelatedElements.add(directionRelatedElement);
                    return;
                }

                if (Objects.nonNull(sourceInstance) && Objects.nonNull(targetInstance)
                        && targetInstance.getId().equals(displayRelatedElementDto.getInstanceId())) {
                    directionRelatedElement.setRelatedElementId(sourceInstance.getId());
                    directionRelatedElement.setRelationShipId(directedRelationship.getId());
                    incomingRelatedElements.add(directionRelatedElement);
                }
            }

        });
    }

    private void relationIsDirectedRelationship(@NotNull DisplayRelatedElementBatchDTO dto,
                                                @NotNull List<MetaClassInstance> directedRelationships,
                                                @NotNull List<DirectionRelatedElement> relatedElements) {
        if (CollectionUtils.isEmpty(directedRelationships)) {
            return;
        }

        final String projectId = dto.getProjectId();
        QueryPropertyForm directedRelationshipsForm = internalUmlService.getQueryPropertyForm(
                directedRelationships,
                Lists.newArrayList(UMLDefinitionConstants.DirectedRelationship_source, UMLDefinitionConstants.DirectedRelationship_target));
        MofResultContainer directedRelationshipsResult = internalUmlService.queryProperty(projectId, directedRelationshipsForm);

        directedRelationships.forEach(directedRelationship -> {
            DirectionRelatedElement directionRelatedElement = new DirectionRelatedElement();
            MetaClassInstance sourceInstance = directedRelationshipsResult.queryListInstanceProperty(
                    directedRelationship.getId(), UMLDefinitionConstants.DirectedRelationship_source).get(0);
            MetaClassInstance targetInstance = directedRelationshipsResult.queryListInstanceProperty(
                    directedRelationship.getId(), UMLDefinitionConstants.DirectedRelationship_target).get(0);

            directionRelatedElement.setRelatedElementId(targetInstance.getId());
            directionRelatedElement.setRelationShipId(directedRelationship.getId());
            directionRelatedElement.setSourceElementId(sourceInstance.getId());
            relatedElements.add(directionRelatedElement);
        });
    }

    /**
     * @param displayRelatedElementDto
     * @param instance                 containment关系
     * @param incomingRelatedElements  入方向的元素
     * @param outgoingRelatedElements  出方向的元素
     * @return void
     * @description 根据包含关系一端和方向，获取包含关系另一端元素
     * @author baibf
     * @date: 2022/5/18
     */
    private void gainContainmentElement(DisplayRelatedElementDTO displayRelatedElementDto, MetaClassInstance instance,
                                        List<DirectionRelatedElement> incomingRelatedElements,
                                        List<DirectionRelatedElement> outgoingRelatedElements) {
        LogUtil.printSerializeOnlineLogs("displayRelatedElementDto: {}, instance: {}, incomingRelatedElements: {}, outgoingRelatedElements: {}",
                displayRelatedElementDto, instance, incomingRelatedElements, outgoingRelatedElements);
        final String projectId = displayRelatedElementDto.getProjectId();
        DirectionRelatedElement directionRelatedElement = new DirectionRelatedElement();
        QueryPropertyForm instanceForm = internalUmlService.getQueryPropertyForm(instance,
                Stream.of(UMLDefinitionConstants.Element_ownedElement, UMLDefinitionConstants.NamedElement_namespace)
                        .collect(Collectors.toList()));
        MofResultContainer instanceResult = internalUmlService.queryProperty(projectId, instanceForm);

        List<MetaClassInstance> ownedElementInstances = instanceResult.queryListInstanceProperty(
                instance.getId(), UMLDefinitionConstants.Element_ownedElement);
        MetaClassInstance namespaceInstance = instanceResult.queryInstanceProperty(
                instance.getId(), UMLDefinitionConstants.NamedElement_namespace);
        if (INCOMING.equals(displayRelatedElementDto.getRelationDirection())
                && CollectionUtils.isNotEmpty(ownedElementInstances)
                && !internalUmlService.checkInstanceType(projectId, instance, UMLDefinitionConstants.Stereotype)) {
            QueryPropertyForm ownedElementsForm = internalUmlService.getQueryPropertyForm(
                    ownedElementInstances, UMLDefinitionConstants.NamedElement_namespace);
            MofResultContainer ownedElementsResult = internalUmlService.queryProperty(projectId, ownedElementsForm);
            ownedElementInstances.forEach(ownedElement -> {
                DirectionRelatedElement directionRelatedElementContainment = new DirectionRelatedElement();
                MetaClassInstance ownedElementNamespaceInstance = ownedElementsResult
                        .queryInstanceProperty(ownedElement.getId(), UMLDefinitionConstants.NamedElement_namespace);
                Set<String> ownedElementGivenTypes = internalUmlService.getGivenTypes(projectId, ownedElement);
                if (Objects.nonNull(ownedElementNamespaceInstance)
                        && ownedElementNamespaceInstance.getId().equals(instance.getId())
                        && (ownedElementGivenTypes.contains(UMLDefinitionConstants.Package)
                        || ownedElementGivenTypes.contains(UMLDefinitionConstants.Classifier))) {
                    directionRelatedElementContainment.setRelatedElementId(ownedElement.getId());
                    directionRelatedElementContainment.setRelationShipId(ModelConstantURI.CONTAINMENT);
                    incomingRelatedElements.add(directionRelatedElementContainment);
                }
            });
            return;
        }

        if (OUTGOING.equals(displayRelatedElementDto.getRelationDirection())
                && Objects.nonNull(namespaceInstance)) {
            directionRelatedElement.setRelatedElementId(namespaceInstance.getId());
            directionRelatedElement.setRelationShipId(ModelConstantURI.CONTAINMENT);
            outgoingRelatedElements.add(directionRelatedElement);
            return;
        }

        if (BOTH.equals(displayRelatedElementDto.getRelationDirection())) {
            containmentDirectionIsBoth(projectId, ownedElementInstances,
                    instance, namespaceInstance, directionRelatedElement, incomingRelatedElements,
                    outgoingRelatedElements);
        }
    }

    private void gainContainmentElement(DisplayRelatedElementBatchDTO dto, List<MetaClassInstance> instances, List<DirectionRelatedElement> relatedElements) {
        final String projectId = dto.getProjectId();
        QueryPropertyForm instanceForm = internalUmlService.getQueryPropertyForm(instances, Lists.newArrayList(UMLDefinitionConstants.Element_ownedElement, UMLDefinitionConstants.NamedElement_namespace));
        MofResultContainer instanceResult = internalUmlService.queryProperty(projectId, instanceForm);
        String relationDirection = dto.getRelationDirection();

        //如果NamedElement_namespace为空认为走到了最上层
        for (Iterator<MetaClassInstance> iterator = instances.iterator(); iterator.hasNext(); ) {
            MetaClassInstance instance = iterator.next();
            MetaClassInstance metaClassInstance = instanceResult.queryInstanceProperty(instance.getId(), UMLDefinitionConstants.NamedElement_namespace);
            if(metaClassInstance==null){
                iterator.remove();
            }
        }

        if (Objects.equals(relationDirection, INCOMING) || Objects.equals(relationDirection, BOTH)) {
            for (MetaClassInstance instance : instances) {
                List<MetaClassInstance> ownedElementInstances = instanceResult.queryListInstanceProperty(instance.getId(), UMLDefinitionConstants.Element_ownedElement);
                if (CollectionUtils.isNotEmpty(ownedElementInstances) && !internalUmlService.checkInstanceType(projectId, instance, UMLDefinitionConstants.Stereotype)) {
                    QueryPropertyForm ownedElementsForm = internalUmlService.getQueryPropertyForm(ownedElementInstances, UMLDefinitionConstants.NamedElement_namespace);
                    MofResultContainer ownedElementsResult = internalUmlService.queryProperty(projectId, ownedElementsForm);
                    ownedElementInstances.forEach(ownedElement -> {
                        DirectionRelatedElement directionRelatedElementContainment = new DirectionRelatedElement();
                        MetaClassInstance ownedElementNamespaceInstance = ownedElementsResult.queryInstanceProperty(ownedElement.getId(), UMLDefinitionConstants.NamedElement_namespace);
                        Set<String> ownedElementGivenTypes = internalUmlService.getGivenTypes(projectId, ownedElement);
                        if (Objects.nonNull(ownedElementNamespaceInstance) && (ownedElementGivenTypes.contains(UMLDefinitionConstants.Package) || ownedElementGivenTypes.contains(UMLDefinitionConstants.Classifier))) {
                            directionRelatedElementContainment.setRelatedElementId(ownedElementNamespaceInstance.getUUID());
                            directionRelatedElementContainment.setRelationShipId(ModelConstantURI.CONTAINMENT);
                            directionRelatedElementContainment.setSourceElementId(ownedElement.getId());
                            relatedElements.add(directionRelatedElementContainment);
                        }
                    });
                }
            }
        }


        if (Objects.equals(relationDirection, OUTGOING) || Objects.equals(relationDirection, BOTH)) {
            for (MetaClassInstance instance : instances) {
                MetaClassInstance metaClassInstance = instanceResult.queryInstanceProperty(instance.getId(), UMLDefinitionConstants.NamedElement_namespace);
                if (metaClassInstance != null) {
                    DirectionRelatedElement directionRelatedElementContainment = new DirectionRelatedElement();
                    directionRelatedElementContainment.setRelatedElementId(metaClassInstance.getUUID());
                    directionRelatedElementContainment.setRelationShipId(ModelConstantURI.CONTAINMENT);
                    directionRelatedElementContainment.setSourceElementId(instance.getId());
                    relatedElements.add(directionRelatedElementContainment);
                }
            }
        }

    }

    private void containmentDirectionIsBoth(String projectId, List<MetaClassInstance> ownedElementInstances,
                                            MetaClassInstance instance, MetaClassInstance namespaceInstance,
                                            DirectionRelatedElement directionRelatedElement,
                                            List<DirectionRelatedElement> incomingRelatedElements,
                                            List<DirectionRelatedElement> outgoingRelatedElements) {
        LogUtil.printSerializeOnlineLogs("projectId, ownedElementInstances, instance,  namespaceInstance, directionRelatedElement",
                projectId, ownedElementInstances, instance, namespaceInstance, directionRelatedElement);
        Set<String> instanceGivenTypes = internalUmlService.getGivenTypes(projectId, instance);
        if (CollectionUtils.isNotEmpty(ownedElementInstances) && !instanceGivenTypes.contains(UMLDefinitionConstants.Stereotype)) {
            QueryPropertyForm ownedElementsForm = internalUmlService.getQueryPropertyForm(
                    ownedElementInstances, UMLDefinitionConstants.NamedElement_namespace);
            MofResultContainer ownedElementsResult = internalUmlService.queryProperty(projectId, ownedElementsForm);

            ownedElementInstances.forEach(ownedElement -> {
                DirectionRelatedElement directionRelatedElementContainment = new DirectionRelatedElement();
                MetaClassInstance ownedElementNamespaceInstance = ownedElementsResult
                        .queryInstanceProperty(ownedElement.getId(), UMLDefinitionConstants.NamedElement_namespace);
                Set<String> ownedElementGivenTypes = internalUmlService.getGivenTypes(projectId, ownedElement);
                if (ownedElementNamespaceInstance != null
                        && (ownedElementGivenTypes.contains(UMLDefinitionConstants.Package)
                        || (ownedElementGivenTypes.contains(UMLDefinitionConstants.Classifier)))
                        && ownedElementNamespaceInstance.getId().equals(instance.getId())
                        && !ownedElementGivenTypes.contains(UMLDefinitionConstants.Relationship)) {
                    directionRelatedElementContainment.setRelatedElementId(ownedElement.getId());
                    directionRelatedElementContainment.setRelationShipId(ModelConstantURI.CONTAINMENT);
                    incomingRelatedElements.add(directionRelatedElementContainment);
                }

            });
        }
        if ((instanceGivenTypes.contains(UMLDefinitionConstants.Package)
                || instanceGivenTypes.contains(UMLDefinitionConstants.Classifier))
                && Objects.nonNull(namespaceInstance)) {
            directionRelatedElement.setRelatedElementId(namespaceInstance.getId());
            directionRelatedElement.setRelationShipId(ModelConstantURI.CONTAINMENT);
            outgoingRelatedElements.add(directionRelatedElement);
        }
    }

    /**
     * @param projectId 项目Id
     * @param instance  模型
     * @return com.huawang.cmof.model.instances.MetaClassInstance instance所属包模型
     * @description 查询一个模型所属的包
     * @author baibf
     * @date 2022/4/25
     */
    @Nullable
    private MetaClassInstance elementOfPackage(String projectId, MetaClassInstance instance) {
        LogUtil.printSerializeOnlineLogs("projectId: {}, instance: {}", projectId, instance);
        MetaClassInstance ownerInstance = internalUmlService.queryInstanceProperty(projectId, instance,
                UMLDefinitionConstants.Element_owner);
        if (internalUmlService.checkInstanceType(projectId, ownerInstance, UMLDefinitionConstants.Package)) {
            return ownerInstance;
        }
        return elementOfPackage(projectId, ownerInstance);
    }

    @NotNull
    private List<MetaClassInstance> gainIncludeEndRelationShip(DisplayRelatedElementDTO displayRelatedElementDto) {
        LogUtil.printSerializeOnlineLogs("gainIncludeEndRelationShip -> displayRelatedElementDto: ", displayRelatedElementDto);
        final String projectId = displayRelatedElementDto.getProjectId();
        MetaClassInstance elementInstance = internalUmlService.fetchInstance(displayRelatedElementDto.getInstanceId(),
                projectId);
        // 获取一端是指定元素的关系
        Set<MetaClassInstance> relationInstances = new HashSet<>();
        relationInstances.addAll(internalUmlService.queryResolveRelationShips(projectId, elementInstance,
                RelationConstraints.RELATION_TYPES));
        relationInstances.addAll(internalUmlService.queryAscendRelationShips(projectId, elementInstance,
                RelationConstraints.RELATION_TYPES));
        // 部件属性设置类型时会创建定向组合关系，显示关联元素时应该排除此关系
        // 模块之间创建关联关系，关系两端实际是引用属性，引用属性显示关联元素时应该排除此关系
        Map<String, Set<String>> relationGivenTypes = internalUmlService.getGivenTypes(projectId,
                new ArrayList<>(relationInstances));
        if (internalUmlService.checkInstanceType(projectId, elementInstance, UMLDefinitionConstants.Customization_PartProperty)
                || internalUmlService.checkInstanceType(projectId, elementInstance, UMLDefinitionConstants.Customization_ReferenceProperty)) {
            relationInstances.removeIf(it -> relationGivenTypes.get(it.getId()).contains(UMLDefinitionConstants.Association));
        }

        // 通过queryResolveRelationShips()查询Connector查到的是ConnectorEnd，因此Connector的查询还需要全量查询并匹配
        List<MetaClassInstance> endIsAssignedElementRelations = getEndIsAssignedElementRelations(projectId,
                Collections.singletonList(elementInstance),
                relationInstances);
        // 移除一端中已在画布中存在的元素
        if (CollectionUtils.isNotEmpty(displayRelatedElementDto.getExcludeRelationIds())) {
            List<MetaClassInstance> excludeRelations = internalUmlService.fetchInstanceByIds(
                    displayRelatedElementDto.getExcludeRelationIds(), projectId);
            endIsAssignedElementRelations.removeAll(excludeRelations);
        }
        return endIsAssignedElementRelations;
    }

    private List<MetaClassInstance> getEndIsAssignedElementRelations(String projectId, List<MetaClassInstance> metaClassInstances,
                                                                     Set<MetaClassInstance> relationInstances) {
        List<MetaClassInstance> connectors = internalUmlService.querySpecifiedInstanceList(projectId,
                UMLDefinitionConstants.Connector, false);
        relationInstances.addAll(connectors);
        Map<String, Set<String>> givenTypes = internalUmlService.getGivenTypes(projectId,
                new ArrayList<>(relationInstances));

        QueryPropertyForm relationForm = internalUmlService.getQueryPropertyForm(
                new ArrayList<>(relationInstances), UMLDefinitionConstants.Connector_end);
        MofResultContainer relationResult = internalUmlService.queryProperty(projectId, relationForm);
        return relationInstances.stream().filter(relation -> {
            if (givenTypes.get(relation.getId()).contains(UMLDefinitionConstants.Connector)) {
                List<MetaClassInstance> endElements = relationResult.queryListInstanceProperty(
                        relation.getId(), UMLDefinitionConstants.Connector_end);
                QueryPropertyForm endElementForm = internalUmlService.getQueryPropertyForm(
                        endElements, UMLDefinitionConstants.ConnectorEnd_role);
                MofResultContainer endElementResult = internalUmlService.queryProperty(projectId, endElementForm);
                List<MetaClassInstance> roleElements = endElements.stream().map(end ->
                        endElementResult.queryInstanceProperty(end.getId(), UMLDefinitionConstants.ConnectorEnd_role))
                        .collect(Collectors.toList());
                return CollUtil.containsAny(roleElements, metaClassInstances);
            }
            return true;
        }).collect(Collectors.toList());
    }

    private List<MetaClassInstance> batchGainIncludeEndRelationShip(DisplayRelatedElementBatchDTO dto) {
        LogUtil.printSerializeOnlineLogs("gainIncludeEndRelationShip -> dto: ", dto);
        final String projectId = dto.getProjectId();
        Map<String, MetaClassInstance> fetchInstances = internalUmlService.fetchInstances(dto.getInstanceIds(), projectId);
        // 获取元素所有的关系
        List<MetaClassInstance> metaClassInstances = Lists.newArrayList(fetchInstances.values());
        Set<MetaClassInstance> relationInstances = Sets.newHashSet();

        if (INCOMING.equals(dto.getRelationDirection())) {
            Map<String, List<MetaClassInstance>> resolveRelationShipMap = internalUmlService.queryResolveRelationShips(projectId, metaClassInstances, RelationConstraints.RELATION_TYPES);
            relationInstances.addAll(resolveRelationShipMap.values().stream().flatMap(Collection::stream).collect(Collectors.toSet()));
        } else if (OUTGOING.equals(dto.getRelationDirection())) {
            Map<String, List<MetaClassInstance>> ascendRelationShipMap = internalUmlService.queryAscendRelationShips(projectId, metaClassInstances, RelationConstraints.RELATION_TYPES);
            relationInstances.addAll(ascendRelationShipMap.values().stream().flatMap(Collection::stream).collect(Collectors.toSet()));
        } else {
            Map<String, List<MetaClassInstance>> ascendRelationShipMap = internalUmlService.queryAscendRelationShips(projectId, metaClassInstances, RelationConstraints.RELATION_TYPES);
            relationInstances.addAll(ascendRelationShipMap.values().stream().flatMap(Collection::stream).collect(Collectors.toSet()));
            Map<String, List<MetaClassInstance>> resolveRelationShipMap = internalUmlService.queryResolveRelationShips(projectId, metaClassInstances, RelationConstraints.RELATION_TYPES);
            relationInstances.addAll(resolveRelationShipMap.values().stream().flatMap(Collection::stream).collect(Collectors.toSet()));
        }
        // 部件属性设置类型时会创建定向组合关系，显示关联元素时应该排除此关系
        // 模块之间创建关联关系，关系两端实际是引用属性，引用属性显示关联元素时应该排除此关系
        relationInstances.removeIf(e ->
                internalUmlService.checkInstanceType(projectId, e, UMLDefinitionConstants.Customization_PartProperty)
                        || internalUmlService.checkInstanceType(projectId, e, UMLDefinitionConstants.Customization_ReferenceProperty));
        // 通过queryResolveRelationShips()查询Connector查到的是ConnectorEnd，因此Connector的查询还需要全量查询并匹配
        List<MetaClassInstance> endIsAssignedElementRelations = getEndIsAssignedElementRelations(projectId,
                metaClassInstances,
                relationInstances);
        // 移除一端中已在画布中存在的元素
        if (CollectionUtils.isNotEmpty(dto.getExcludeRelationIds())) {
            List<MetaClassInstance> excludeRelations = internalUmlService.fetchInstanceByIds(dto.getExcludeRelationIds(), projectId);
            endIsAssignedElementRelations.removeAll(excludeRelations);
        }
        return endIsAssignedElementRelations;
    }

    @Nullable
    public RelationshipVo queryEnds(String projectId, String instanceId) {
        LogUtil.printOnlineLogs("queryEnds -> projectId: {}, instanceId: {}", projectId, instanceId);
        MetaClassInstance instance = internalUmlService.fetchInstance(instanceId, projectId);
        if (instance == null) {
            return null;
        }

        return fillRelationElement(projectId, instance);
    }

    @NotNull
    public Boolean checkCreateGeneral(String projectId, String sourceId, String targetId) {
        LogUtil.printOnlineLogs("projectId: {}, sourceId: {}, targetId: {}", projectId, sourceId, targetId);
        MetaClassInstance source = internalUmlService.fetchInstance(sourceId, projectId);
        MetaClassInstance target = internalUmlService.fetchInstance(targetId, projectId);
        if (source == null || target == null) {
            return false;
        }

        final Set<String> sourceGivenTypes = internalUmlService.getGivenTypes(projectId, source);
        final Set<String> targetGivenTypes = internalUmlService.getGivenTypes(projectId, target);

        if (sourceGivenTypes.contains(UMLDefinitionConstants.AssociationClass)) {
            return targetGivenTypes.contains(UMLDefinitionConstants.AssociationClass)
                    || targetGivenTypes.contains(UMLDefinitionConstants.SysML_Block)
                    || targetGivenTypes.contains(UMLDefinitionConstants.Association);
        } else if (sourceGivenTypes.contains(UMLDefinitionConstants.Association)) {
            return (targetGivenTypes.contains(UMLDefinitionConstants.SysML_Block)
                    || targetGivenTypes.contains(UMLDefinitionConstants.Association))
                    && !targetGivenTypes.contains(UMLDefinitionConstants.AssociationClass);
        } else {
            if (!Objects.equals(source.getMetaClass().getId(), target.getMetaClass().getId())) {
                return false;
            }
        }
        List<MetaClassInstance> generals = internalUmlService.findGeneralsCascade(projectId, target.getId(), true);
        return !generals.contains(source);
    }

    @NotNull
    public List<String> findSpecificsCascade(String projectId, String instanceId) {
        LogUtil.printOnlineLogs("findSpecificsCascade -> projectId: {}, instanceId: {}", projectId, instanceId);
        MetaClassInstance instance = internalUmlService.fetchInstance(instanceId, projectId);
        if (instance == null) {
            return Collections.emptyList();
        }

        return internalUmlService.findSpecificsCascade(projectId, instance, false).stream()
                .map(BasicRElement::getId).collect(Collectors.toList());
    }

    /**
     * 判断source与target是否与关系对应端type匹配
     * source不为空，target为空验证source是否匹配
     * source与target都不为空验证两端是否匹配
     * @param validateDto
     * @return
     */
    @NotNull
    public Boolean validateRelationCreation(ValidateRelationCreationDto validateDto) {
        return uafValidateUtils.validateRelationCreation(validateDto);
    }

    @NotNull
    public Boolean checkSourceIsSubClassOfTarget(String projectId, String sourceId, String targetId) {
        if(sourceId != null && targetId != null && sourceId.equals(targetId)) {
            return Boolean.FALSE;
        }

        MetaClassInstance source = internalUmlService.fetchInstance(sourceId, projectId);
        MetaClassInstance target = internalUmlService.fetchInstance(targetId, projectId);
        if (source == null || target == null) {
            return Boolean.FALSE;
        }

        List<MetaClassInstance> generals = internalUmlService.findGenerals(projectId, source, false);
        if (generals.contains(target)) {
            return Boolean.TRUE;
        } else {
            if (targetId != null) {
                return internalUmlService.checkInstanceType(projectId, source, targetId);
            } else {
                return Boolean.FALSE;
            }
        }
    }

    public boolean checkEndsCanCreateRelation(String projectId, String sourceId, String targetId, String relationType) {
        LogUtil.printOnlineLogs("projectId: {}, sourceId: {}, targetId: {}, relationType: {}", projectId, sourceId, targetId,
                relationType);
        String sourceMenuKey = diagramConfigService.getMenuKey(projectId, sourceId, managerConfig.getConnectInfoConfig());
        ConnectInfo sourceConnectInfo = managerConfig.getConnectInfoConfig().get(sourceMenuKey);

        String targetMenuKey = diagramConfigService.getMenuKey(projectId, targetId, managerConfig.getConnectInfoConfig());
        ConnectInfo targetConnectInfo = this.managerConfig.getConnectInfoConfig().get(targetMenuKey);

        Map<String, MetaClassInstance> instanceMap = internalUmlService.fetchInstances(
                Stream.of(sourceId, targetId).collect(Collectors.toList()), projectId);
        if (Objects.nonNull(sourceConnectInfo) && Objects.nonNull(targetConnectInfo)) {
            List<String> sourceCustomOutgoing = customizationService.queryCanCreatedRelation(
                            projectId, instanceMap.get(sourceId), RelationExtendTypeEnum.OUTGOING)
                    .stream().map(BasicRElement::getId).collect(Collectors.toList());
            List<String> targetCustomIncoming = customizationService.queryCanCreatedRelation(
                            projectId, instanceMap.get(targetId), RelationExtendTypeEnum.INCOMING)
                    .stream().map(BasicRElement::getId).collect(Collectors.toList());
            // source取outgoing，target取incoming
            List<String> sourceOutgoing = sourceConnectInfo.getOutgoing();
            List<String> targetIncoming = targetConnectInfo.getIncoming();
            List<String> endCanCreateRelationTypeList = (List<String>) CollectionUtils.intersection(sourceOutgoing,
                    targetIncoming);
            List<String> endCanCreateCustomRelationTypeList = (List<String>) CollectionUtils.intersection(sourceCustomOutgoing,
                    targetCustomIncoming);
            Set<String> allCanCreateList = Stream.concat(endCanCreateRelationTypeList.stream(),
                    endCanCreateCustomRelationTypeList.stream()).collect(Collectors.toSet());
            return allCanCreateList.contains(relationType)
                    || allCanCreateList.stream().anyMatch(relationType::contains);
        }
        return false;
    }

    /**
     * desciption:判断关系是否能够改变方向
     */
    @NotNull
    public Boolean canConvertRelationshipDirection(String projectId, String instanceId) {
        LogUtil.printOnlineLogs("canConvertRelationshipDirection -> projectId: {}, instanceId: {}", projectId, instanceId);
        MetaClassInstance relationship = internalUmlService.fetchInstance(instanceId, projectId);
        if (Objects.isNull(relationship)) {
            log.error("关系模型查找失败");
            throw AbortExecutionException.build(ErrorCode.MODEL_NO_EXIST);
        }
        Set<String> givenTypes = internalUmlService.getGivenTypes(projectId, relationship);
        // 1.对象流、消息、拓展、连接器(本期)及其拓展子类不可变更连线方向
        if (givenTypes.contains(UMLDefinitionConstants.ObjectFlow) || givenTypes.contains(UMLDefinitionConstants.Message) ||
                givenTypes.contains(UMLDefinitionConstants.Extension) || givenTypes.contains(UMLDefinitionConstants.Connector)) {
            return false;
        }

        // 2.源和目标为同一模型不可变更:一、Association特殊处理(两端模型类型相等视为同一模型) 二、非Association：queryRelationSources/queryRelationTargets相等
        if (givenTypes.contains(UMLDefinitionConstants.Association)) {
            return canConvertAssociation(projectId, relationship);
        } else {
            return canConvertNonAssociation(projectId, relationship);
        }
    }

    private boolean canConvertAssociation(@NotNull String projectId, @NotNull MetaClassInstance relationship) {
        LogUtil.printSerializeOnlineLogs("projectId:  {}, relationship: {}", projectId, relationship);
        String relationType = diagramConfigService.getMenuKey(projectId, relationship.getId(), managerConfig.getConnectInfoConfig());
        List<MetaClassInstance> memberEnds = internalUmlService.queryListInstanceProperty(projectId, relationship,
                UMLDefinitionConstants.Association_memberEnd);
        Map<String, ModelPropertyInfo> modelPropertyInfoMap = modelPropertyInfoService.createModelPropertyInfoMap(projectId, memberEnds,
                Collections.singletonList(UMLDefinitionConstants.TypedElement_type));
        ModelPropertyInfo sourcePropertyInfo = modelPropertyInfoMap.get(memberEnds.get(0).getId());
        ModelPropertyInfo targetPropertyInfo = modelPropertyInfoMap.get(memberEnds.get(1).getId());
        MetaClassInstance sourceEndType = sourcePropertyInfo.queryInstanceValue(UMLDefinitionConstants.TypedElement_type);
        MetaClassInstance targetEndType = targetPropertyInfo.queryInstanceValue(UMLDefinitionConstants.TypedElement_type);
        if (Objects.equals(sourceEndType, targetEndType)) {
            return false;
        }
        if (Objects.isNull(sourceEndType) || Objects.isNull(targetEndType)) {
            log.error("Association两端属性类型查询不存在");
            throw AbortExecutionException.build(ErrorCode.QUERY_MODEL_ERROR);
        }
        // 无向Association连线不可变更方向
        if (BooleanUtils.isFalse(internalUmlService.isDirectOfAssociation(projectId, relationship))) {
            return false;
        }
        // 变更后连线源与目标不在允许连接范围内:业务配置文件
        return !BooleanUtils.isFalse(checkEndsCanCreateRelation(projectId, targetEndType.getId(), sourceEndType.getId(), relationType));
    }

    private boolean canConvertNonAssociation(@NotNull String projectId, @NotNull MetaClassInstance relationship) {
        LogUtil.printSerializeOnlineLogs("projectId:  {}, relationship: {}", projectId, relationship);
        String relationType = diagramConfigService.getMenuKey(projectId, relationship.getId(), managerConfig.getConnectInfoConfig());
        Map<MetaClassInstance, List<MetaClassInstance>> relationSourcesMap = internalUmlService.queryRelationSources(
                projectId, Collections.singletonList(relationship));
        Map<MetaClassInstance, List<MetaClassInstance>> relationTargetsMap = internalUmlService.queryRelationTargets(
                projectId, Collections.singletonList(relationship));
        MetaClassInstance relationSource = relationSourcesMap.get(relationship).get(0);
        MetaClassInstance relationTarget = relationTargetsMap.get(relationship).get(0);
        if (Objects.equals(relationSource, relationTarget)) {
            return false;
        }
        // 变更后连线源与目标不在允许连接范围内:业务配置文件
        if (BooleanUtils.isFalse(checkEndsCanCreateRelation(projectId, relationTarget.getId(), relationSource.getId(), relationType))) {
            return false;
        }
        // 部分模型存在特殊限制
        // 一、判断节点与合并节点出入关系数量存在限制
        // 二、分支、集合出入关系数量存在限制
        // 后续持续补充...
        return !BooleanUtils.isFalse(
                canConvertWithSpecialSourceEnd(projectId, relationship, relationSource) &&
                        canConvertWithSpecialTargetEnd(projectId, relationship, relationTarget));
    }

    /**
     * description:处理Source端部分模型特殊限制
     *
     * @param projectId
     * @param relationship
     * @param endModel     :关系端模型
     * @return boolean
     * @author lx
     * @date 2024/2/23
     */
    private boolean canConvertWithSpecialSourceEnd(@NotNull String projectId,
                                                   @NotNull MetaClassInstance relationship,
                                                   @NotNull MetaClassInstance endModel) {
        LogUtil.printSerializeOnlineLogs("projectId: {}, relationship: {}, endModel: {}", projectId, relationship, endModel);
        boolean canConvertDirection;
        if (internalUmlService.checkInstanceType(projectId, relationship, UMLDefinitionConstants.ActivityEdge)) {
            MofResultContainer activityNodeContainer = internalUmlService.queryProperty(projectId,
                    internalUmlService.getQueryPropertyForm(endModel,
                            Stream.of(UMLDefinitionConstants.ActivityNode_incoming).collect(Collectors.toList())));
            // 1.如果source端为判断节点: 入方向+1, 判断节点入方向数量小于2才能变更
            // 2.如果source端为分支: 入方向+1, 判断节点入方向为空才能变更
            List<MetaClassInstance> incomingList = activityNodeContainer.queryListInstanceProperty(
                    endModel.getId(), UMLDefinitionConstants.ActivityNode_incoming);
            if (internalUmlService.checkInstanceType(projectId, endModel, UMLDefinitionConstants.DecisionNode)) {
                canConvertDirection = incomingList.size() <= 1;
            } else if (internalUmlService.checkInstanceType(projectId, endModel, UMLDefinitionConstants.ForkNode)) {
                canConvertDirection = CollectionUtils.isEmpty(incomingList);
            } else {
                canConvertDirection = true;
            }
        } else if (internalUmlService.checkInstanceType(projectId, relationship, UMLDefinitionConstants.Transition)) {
            MofResultContainer stateContainer = internalUmlService.queryProperty(projectId,
                    internalUmlService.getQueryPropertyForm(endModel,
                            Stream.of(UMLDefinitionConstants.Vertex_incoming,
                                    UMLDefinitionConstants.Pseudostate_kind).collect(Collectors.toList())));
            List<MetaClassInstance> incomingList = stateContainer.queryListInstanceProperty(
                    endModel.getId(), UMLDefinitionConstants.Vertex_incoming);
            MetaClassInstance kind = stateContainer.queryInstanceProperty(endModel.getId(), UMLDefinitionConstants.Pseudostate_kind);
            if (Objects.nonNull(kind) && StringUtils.equals(kind.getId(), UMLDefinitionConstants.PseudostateKind_fork)) {
                canConvertDirection = CollectionUtils.isEmpty(incomingList);
            } else {
                canConvertDirection = true;
            }
        } else {
            canConvertDirection = true;
        }
        return canConvertDirection;
    }

    /**
     * description:处理Target端部分模型特殊限制
     */
    private boolean canConvertWithSpecialTargetEnd(@NotNull String projectId,
                                                   @NotNull MetaClassInstance relationship,
                                                   @NotNull MetaClassInstance endModel) {
        LogUtil.printSerializeOnlineLogs("projectId: {}, relationship: {}, endModel: {}", projectId, relationship, endModel);
        boolean canConvertDirection;
        if (internalUmlService.checkInstanceType(projectId, relationship, UMLDefinitionConstants.ActivityEdge)) {

            MofResultContainer nodeResultContainer = internalUmlService.queryProperty(projectId,
                    internalUmlService.getQueryPropertyForm(endModel,
                            Stream.of(UMLDefinitionConstants.ActivityNode_outgoing).collect(Collectors.toList())));
            // 1.如果target端为合并节点或集合:出方向+1, 出方向为空才能变更
            List<MetaClassInstance> outGoingList = nodeResultContainer.queryListInstanceProperty(
                    endModel.getId(), UMLDefinitionConstants.ActivityNode_outgoing);
            if (internalUmlService.checkInstanceType(projectId, endModel, UMLDefinitionConstants.MergeNode) ||
                    internalUmlService.checkInstanceType(projectId, endModel, UMLDefinitionConstants.JoinNode)) {
                canConvertDirection = CollectionUtils.isEmpty(outGoingList);
            } else {
                canConvertDirection = true;
            }
        } else if (internalUmlService.checkInstanceType(projectId, relationship, UMLDefinitionConstants.Transition)) {
            MofResultContainer stateContainer = internalUmlService.queryProperty(projectId,
                    internalUmlService.getQueryPropertyForm(endModel,
                            Stream.of(UMLDefinitionConstants.Vertex_outgoing,
                                    UMLDefinitionConstants.Pseudostate_kind).collect(Collectors.toList())));
            List<MetaClassInstance> outGoingList = stateContainer.queryListInstanceProperty(
                    endModel.getId(), UMLDefinitionConstants.Vertex_outgoing);
            MetaClassInstance kind = stateContainer.queryInstanceProperty(endModel.getId(), UMLDefinitionConstants.Pseudostate_kind);
            if (Objects.nonNull(kind) && StringUtils.equals(kind.getId(), UMLDefinitionConstants.PseudostateKind_join)) {
                canConvertDirection = CollectionUtils.isEmpty(outGoingList);
            } else {
                canConvertDirection = true;
            }
        } else {
            canConvertDirection = true;
        }
        return canConvertDirection;
    }

    @NotNull
    public ModelResultVo changeRelationshipDirection(ChangeRelationshipDirectionDTO dto) {
        LogUtil.printSerializeOnlineLogs("changeRelationshipDirection -> dto: ", dto);
        final String projectId = dto.getProjectId();
        final String relationshipId = dto.getInstanceId();
        MetaClassInstance relationship = checkRelationShipIsExist(projectId, relationshipId);

        LocalTransaction transaction = transactionService.createTransaction(projectId, UPDATE_MODEL);
        ModelResultVo modelResultVo = new ModelResultVo();
        try {
            Set<String> givenTypes = internalUmlService.getGivenTypes(projectId, relationship);
            if (givenTypes.contains(UMLDefinitionConstants.InformationFlow)) {
                // 变更流两端
                changeItemFlowDirection(projectId, relationship);
            } else {
                // 变更连线两端
                removeItemFlowOnRelationship(projectId, relationship, true);
                // 1.queryRelationSources/queryRelationTargets查询两端 2.updateRelationEnds更新两端
                RelationEnd relationEnd = findRelationEnds(projectId, relationship);
                if (givenTypes.contains(UMLDefinitionConstants.Association)) {
                    MetaClassInstance oldSourceType = relationEnd.getSourceType();
                    MetaClassInstance oldTargetType = relationEnd.getTargetType();
                    internalRelationService.changeRelationEnd(projectId, relationshipId, oldSourceType.getId(), oldTargetType.getId());
                } else {
                    MetaClassInstance oldSource = relationEnd.getSource();
                    MetaClassInstance oldTarget = relationEnd.getTarget();
                    internalRelationService.changeRelationEnd(projectId, relationshipId, oldTarget.getId(), oldSource.getId());
                }
                RelationEnd newEnd = findRelationEnds(projectId, relationship);
                updateRelationPostHandle(projectId, relationship, newEnd);
            }
            transactionService.commit(projectId);
            modelResultVo.setInstanceId(relationshipId);
            InstanceVoUtils.fillModelResultVo(modelResultVo, transaction);
        } catch (TransactionNotExistingException | LocalTransactionNotStartException e) {
            throw AbortExecutionException.build(ErrorCode.TRANSACTION_ERROR, e);
        } catch (Exception e) {
            transactionService.rollBack(projectId, transaction);
            throw AbortExecutionException.build(ErrorCode.UPDATE_MODEL_ERROR, e);
        }
        return modelResultVo;
    }


    /**
     * desciption:变更项流方向
     */
    private void changeItemFlowDirection(@NotNull String projectId,
                                         @NotNull MetaClassInstance itemFlow) {
        LogUtil.printSerializeOnlineLogs("projectId: {}, itemFlow: {}", projectId, itemFlow);
        ModelPropertyInfo modelPropertyInfo = modelPropertyInfoService.createSimpleModelPropertyInfo(projectId, itemFlow,
                Stream.of(UMLDefinitionConstants.InformationFlow_realization,
                        UMLDefinitionConstants.InformationFlow_realizingConnector,
                        UMLDefinitionConstants.InformationFlow_realizingActivityEdge,
                        UMLDefinitionConstants.InformationFlow_realizingMessage,
                        UMLDefinitionConstants.InformationFlow_informationSource,
                        UMLDefinitionConstants.InformationFlow_informationTarget).collect(Collectors.toList()));
        List<MetaClassInstance> realizationAssociations = modelPropertyInfo.queryListInstanceValue(UMLDefinitionConstants.InformationFlow_realization);
        List<MetaClassInstance> realizingActivityEdge = modelPropertyInfo.queryListInstanceValue(UMLDefinitionConstants.InformationFlow_realizingActivityEdge);
        List<MetaClassInstance> realizingMessage = modelPropertyInfo.queryListInstanceValue(UMLDefinitionConstants.InformationFlow_realizingMessage);
        MetaClassInstance itemFlowSource = modelPropertyInfo.queryListInstanceValue(UMLDefinitionConstants.InformationFlow_informationSource).get(0);
        MetaClassInstance itemFlowTarget = modelPropertyInfo.queryListInstanceValue(UMLDefinitionConstants.InformationFlow_informationTarget).get(0);

        if (Objects.equals(itemFlowSource, itemFlowTarget)) {
            log.error("项流源和目标相同,不允许变更项流两端");
            throw AbortExecutionException.build(ErrorCode.ITEM_FLOW_SOURCE_TARGET_ERROR);
        }
        if (CollectionUtils.isNotEmpty(realizingActivityEdge)) {
            log.error("实现活动边或实现消息不为空，不允许变更流方向");
            throw AbortExecutionException.build(ErrorCode.ITEM_FLOW_CONVERT_ERROR);
        }
        if (Objects.isNull(itemFlowSource) || Objects.isNull(itemFlowTarget)) {
            log.error("项流两端有一端不存在");
            throw AbortExecutionException.build(ErrorCode.MODEL_NO_EXIST);
        }
        Map<String, ModelPropertyInfo> realizationPropertyInfoMap = modelPropertyInfoService.createModelPropertyInfoMap(projectId,
                realizationAssociations, Collections.singletonList(UMLDefinitionConstants.Association_endType));

        Set<MetaClassInstance> endsSet = realizationAssociations.stream().flatMap(it -> {
            ModelPropertyInfo realizationPropertyInfo = realizationPropertyInfoMap.get(it.getId());
            return realizationPropertyInfo.queryListInstanceValue(UMLDefinitionConstants.Association_endType).stream();
        }).collect(Collectors.toSet());
        if (CollectionUtils.isNotEmpty(realizationAssociations) && !Objects.equals(endsSet.size(), 2)) {
            log.error("多条实现边间的source与target存在不一致");
            throw AbortExecutionException.build(ErrorCode.ITEM_FLOW_CONVERT_ERROR);
        }

        // 变更项流两端
        internalRelationService.changeRelationEnd(projectId, itemFlow.getId(), itemFlowTarget.getId(), itemFlowSource.getId());

        List<MetaClassInstance> endTypes = Stream.of(itemFlowSource, itemFlowTarget).collect(Collectors.toList());
        List<MetaClassInstance> removeAssociations;
        if (endsSet.containsAll(endTypes)) {
            removeAssociations = realizationAssociations.stream().filter(it -> {
                ModelPropertyInfo realizationPropertyInfo = realizationPropertyInfoMap.get(it.getId());
                List<MetaClassInstance> associationEndTypes = realizationPropertyInfo.queryListInstanceValue(UMLDefinitionConstants.Association_endType);
                // 如果关联关系 有向且与项流变更后源目标不一致，则移除
                return internalUmlService.isDirectOfAssociation(projectId, it) && !associationEndTypes.equals(endTypes);
            }).collect(Collectors.toList());
        } else {
            removeAssociations = realizationAssociations;
        }
        umlService.removeMultipleModel(projectId, itemFlow.getId(), UMLDefinitionConstants.InformationFlow_realization, removeAssociations);
        umlService.removeMultipleModel(projectId, itemFlow.getId(), UMLDefinitionConstants.InformationFlow_realizingMessage, realizingMessage);
    }

    @NotNull
    public List<SelectElementItem> getAllRequirementRelations(String projectId) {
        MetaClassInstance traceInstance = internalUmlService.fetchInstance(UMLDefinitionConstants.SysML_Trace, projectId);
        if (traceInstance == null) {
            return Collections.emptyList();
        }

        MetaClassInstance refineInstance = internalUmlService.fetchInstance(UMLDefinitionConstants.SysML_Refine, projectId);
        if (refineInstance == null) {
            return Collections.emptyList();
        }

        List<MetaClassInstance> specificsCascade = internalUmlService.findSpecificsCascade(projectId, traceInstance, true);
        specificsCascade.addAll(internalUmlService.findSpecificsCascade(projectId, refineInstance, true));
        specificsCascade = specificsCascade.stream().filter(it -> !UMLDefinitionConstants.Customization_CommandInterval.equals(it.getId())).collect(Collectors.toList());

        List<ModelPropertyInfo> activityParameterNodeModels = CollectionUtils.isNotEmpty(specificsCascade) ?
                modelPropertyInfoService.createModelPropertyInfoList(
                        projectId, specificsCascade,
                        Stream.of(UMLDefinitionConstants.NamedElement_name).collect(Collectors.toList())) :
                new ArrayList<>(0);

        final Map<String, String> iconMap = this.modelPropertyInfoService.queryIconBatch(projectId, specificsCascade);

        return activityParameterNodeModels.stream().map(model -> {
            SelectElementItem selectElementItem = new SelectElementItem();
            selectElementItem.setInstanceId(model.getInstance().getId());
            selectElementItem.setName(model.queryStringValue(UMLDefinitionConstants.NamedElement_name));
            selectElementItem.setIcon(iconMap.get(model.getInstanceId()));
            return selectElementItem;
        }).collect(Collectors.toList());
    }

    @NotNull
    public ModelResultVo changeRelationshipEnds(ChangeRelationshipEndsDTO dto) {
        LogUtil.printSerializeOnlineLogs("changeRelationshipEnds -> dto: ", dto);
        final String projectId = dto.getProjectId();
        final String relationshipId = dto.getInstanceId();
        MetaClassInstance relationship = checkRelationShipIsExist(projectId, relationshipId);

        LocalTransaction transaction = transactionService.createTransaction(projectId, UPDATE_MODEL);
        ModelResultVo vo = new ModelResultVo();
        try {
            // 1.移除变更边上对应项流
            removeItemFlowOnRelationship(projectId, relationship, false);
            // 2.部分模型连接关系前的业务逻辑
            ChangeRelationshipEndsDTO newDto = initRelationEnds(projectId, relationship, dto);
            // 3.连线变更两端:Association特殊处理，传入的是两端的type
            internalRelationService.changeRelationEnd(projectId, relationshipId, newDto.getSourceId(), newDto.getTargetId());
            // 4.查询边变更后的source/target,并进行附带业务逻辑属性变更
            RelationEnd newRelationEnd = findRelationEnds(projectId, relationship);
            updateRelationPostHandle(projectId, relationship, newRelationEnd);
            transactionService.commit(projectId);
            vo.setInstanceId(relationshipId);
            InstanceVoUtils.fillModelResultVo(vo, transaction);
        } catch (TransactionNotExistingException | LocalTransactionNotStartException e) {
            throw AbortExecutionException.build(ErrorCode.TRANSACTION_ERROR, e);
        } catch (Exception e) {
            transactionService.rollBack(projectId, transaction);
            throw AbortExecutionException.build(ErrorCode.SERVICE_ERROR, e);
        }
        return vo;
    }

    @NotNull
    private MetaClassInstance checkRelationShipIsExist(String projectId, String relationshipId) {
        MetaClassInstance relationship = internalUmlService.fetchInstance(relationshipId, projectId);
        if (Objects.isNull(relationship)) {
            log.error("关系模型或其两端查找失败");
            throw AbortExecutionException.build(ErrorCode.MODEL_NO_EXIST);
        }
        return relationship;
    }

    @NotNull
    private ChangeRelationshipEndsDTO initRelationEnds(String projectId,
                                                       MetaClassInstance relation,
                                                       ChangeRelationshipEndsDTO dto) {
        LogUtil.printSerializeOnlineLogs("projectId: {}, relation: {}, dto: {}", projectId, relation, dto);
        MetaClassInstance newSource = internalUmlService.fetchInstance(dto.getSourceId(), projectId);
        MetaClassInstance newTarget = internalUmlService.fetchInstance(dto.getTargetId(), projectId);
        if (internalUmlService.checkInstanceType(projectId, relation, UMLDefinitionConstants.Message)) {
            final String operation = OperationConstants.CALL_MESSAGE;
            // 1.取sort
            final MetaClassInstance messageSort = createMessage.initMessageSort(operation, projectId);
            MetaClassInstance parent = null;
            if (Objects.nonNull(newSource)) {
                parent = createMessage.findInteraction(projectId, newSource);
            }

            if (Objects.isNull(parent) && Objects.nonNull(newTarget)) {
                parent = createMessage.findInteraction(projectId, newTarget);
            }
            if (Objects.isNull(parent)) {
                throw AbortExecutionException.build(ErrorCode.PARAM_ERROR);
            }
            final String parentId = parent.getId();
            MetaClassInstance receiveEvent = null;
            MetaClassInstance sendEvent = null;
            if (Objects.nonNull(newSource)) {
                sendEvent = createMessage.initSendEvent(newSource, projectId, parentId, messageSort);
            }
            if (Objects.nonNull(newTarget)) {
                receiveEvent = createMessage.initReceiveEvent(newTarget, projectId, parentId, messageSort);
            }
            createMessage.updateModel(operation, projectId, messageSort, receiveEvent, sendEvent, relation);
            dto.setSourceId(Objects.nonNull(sendEvent) ? sendEvent.getId() : null);
            dto.setTargetId(Objects.nonNull(receiveEvent) ? receiveEvent.getId() : null);
        }
        return dto;
    }

    @NotNull
    private RelationEnd findRelationEnds(String projectId, MetaClassInstance relationship) {
        LogUtil.printSerializeOnlineLogs("projectId: {}, relationship: {}", projectId, relationship);
        RelationEnd relationEnd = new RelationEnd();
        Map<MetaClassInstance, List<MetaClassInstance>> relationSourcesMap = internalUmlService.queryRelationSources(
                projectId, Collections.singletonList(relationship));
        Map<MetaClassInstance, List<MetaClassInstance>> relationTargetsMap = internalUmlService.queryRelationTargets(
                projectId, Collections.singletonList(relationship));
        List<MetaClassInstance> relationSources = relationSourcesMap.get(relationship);
        List<MetaClassInstance> relationTargets = relationTargetsMap.get(relationship);

        if (CollectionUtils.isEmpty(relationSources) || CollectionUtils.isEmpty(relationTargets)) {
            log.error("关系两端有一端为空");
            throw AbortExecutionException.build(ErrorCode.PROPERTY_QUERY_ERROR);
        }
        Map<String, Set<String>> givenTypesMap = internalUmlService.getGivenTypes(projectId,
                Stream.of(Collections.singletonList(relationship), relationSources, relationTargets)
                        .flatMap(Collection::stream).collect(Collectors.toList()));
        Set<String> relationGivenTypes = internalUmlService.getGivenTypes(projectId, relationship);
        // Association特殊处理
        if (relationGivenTypes.contains(UMLDefinitionConstants.Association)) {
            MetaClassInstance source = relationSources.stream()
                    .filter(it -> givenTypesMap.get(it.getId()).contains(UMLDefinitionConstants.Property))
                    .findFirst().orElse(null);
            MetaClassInstance sourceType = relationSources.stream()
                    .filter(it -> givenTypesMap.get(it.getId()).contains(UMLDefinitionConstants.Type))
                    .findFirst().orElse(null);
            // 无向Association查出的两端相同，所以需要倒置列表再过滤出target端的相关模型
            Collections.reverse(relationTargets);
            MetaClassInstance target = relationTargets.stream()
                    .filter(it -> givenTypesMap.get(it.getId()).contains(UMLDefinitionConstants.Property))
                    .findFirst().orElse(null);
            MetaClassInstance targetType = relationTargets.stream()
                    .filter(it -> givenTypesMap.get(it.getId()).contains(UMLDefinitionConstants.Type))
                    .findFirst().orElse(null);
            if (Objects.isNull(source) || Objects.isNull(target)) {
                log.error("Association有一端不存在类型为type的模型");
                throw AbortExecutionException.build(ErrorCode.MODEL_NO_EXIST);
            }
            relationEnd.setSource(source);
            relationEnd.setSourceType(sourceType);
            relationEnd.setTarget(target);
            relationEnd.setTargetType(targetType);
        } else {
            relationEnd.setSource(relationSources.get(0));
            relationEnd.setTarget(relationTargets.get(0));
        }
        return relationEnd;
    }

    /**
     * desciption:变更连线两端相关属性
     */
    private void updateRelationPostHandle(@NotNull String projectId,
                                          @NotNull MetaClassInstance relation,
                                          @NotNull RelationEnd relationEnd) {
        LogUtil.printSerializeOnlineLogs("projectId: {}, relation: {}, relationEnd: {}", projectId, relation, relationEnd);
        List<UpdateModelDTO> updateModelDTOS = new ArrayList<>();
        MetaClassInstance source = relationEnd.getSource();
        MetaClassInstance sourceType = relationEnd.getSourceType();
        MetaClassInstance target = relationEnd.getTarget();
        MetaClassInstance targetType = relationEnd.getTargetType();

        // 2.Association连线两端属性类型变更后，名称可能需要保持一致
        if (internalUmlService.checkInstanceType(projectId, relation, UMLDefinitionConstants.Association)) {

            List<MetaClassInstance> instances = Stream.of(source, sourceType, target, targetType)
                    .filter(Objects::nonNull).collect(Collectors.toList());
            Map<String, ModelPropertyInfo> modelPropertyInfoMap = modelPropertyInfoService.createModelPropertyInfoMap(
                    projectId, instances, Collections.singletonList(UMLDefinitionConstants.NamedElement_name));
            String sourceName = modelPropertyInfoMap.get(source.getId()).queryStringValue(UMLDefinitionConstants.NamedElement_name);
            String sourceTypeName = modelPropertyInfoMap.get(sourceType.getId()).queryStringValue(UMLDefinitionConstants.NamedElement_name);
            String targetName = modelPropertyInfoMap.get(target.getId()).queryStringValue(UMLDefinitionConstants.NamedElement_name);
            String targetTypeName = modelPropertyInfoMap.get(targetType.getId()).queryStringValue(UMLDefinitionConstants.NamedElement_name);
            if (StringUtils.isNotEmpty(sourceName) && !Objects.equals(sourceName, sourceTypeName)) {
                updateModelDTOS.add(new UpdateModelDTO(projectId, source.getId(), UMLDefinitionConstants.NamedElement_name, sourceTypeName));
            }
            if (StringUtils.isNotEmpty(targetName) && !Objects.equals(targetName, targetTypeName)) {
                updateModelDTOS.add(new UpdateModelDTO(projectId, target.getId(), UMLDefinitionConstants.NamedElement_name, targetTypeName));
            }
        }
        if (CollectionUtils.isNotEmpty(updateModelDTOS)) {
            umlService.updateModelByBatch(updateModelDTOS);
        }
    }

    /**
     * desciption:移除变更边上实现的所有项流
     */
    private void removeItemFlowOnRelationship(String projectId, MetaClassInstance relationship, Boolean isConvertDirection) {
        LogUtil.printSerializeOnlineLogs("projectId: {}, relationship: {}, isConvertDirection: {}", projectId, relationship,
                isConvertDirection);
        // 如果变更边无向，则无需移除关系上存在的项流
        if (BooleanUtils.isTrue(isConvertDirection)
                && internalUmlService.checkInstanceType(projectId, relationship, UMLDefinitionConstants.Association)
                && !internalUmlService.isDirectOfAssociation(projectId, relationship)) {
            return;
        }
        List<MetaClassInstance> allInformationFlows = internalUmlService.querySpecifiedInstanceList(
                projectId, UMLDefinitionConstants.InformationFlow);
        Map<String, ModelPropertyInfo> allInformationFlowsInfoMap = modelPropertyInfoService.createModelPropertyInfoMap(
                projectId, allInformationFlows, Stream.of(
                        UMLDefinitionConstants.InformationFlow_informationSource,
                        UMLDefinitionConstants.InformationFlow_informationTarget,
                        UMLDefinitionConstants.InformationFlow_realization,
                        UMLDefinitionConstants.InformationFlow_realizingConnector,
                        UMLDefinitionConstants.InformationFlow_realizingActivityEdge,
                        UMLDefinitionConstants.InformationFlow_realizingMessage).collect(Collectors.toList()));

        List<RemoveModelDto> removeModelDtos = new ArrayList<>();
        allInformationFlows.forEach(it -> {
            String informationFlowId = it.getId();
            ModelPropertyInfo modelPropertyInfo = allInformationFlowsInfoMap.get(informationFlowId);
            List<MetaClassInstance> realizations = modelPropertyInfo.queryListInstanceValue(
                    UMLDefinitionConstants.InformationFlow_realization);
            List<MetaClassInstance> realizingConnectors = modelPropertyInfo.queryListInstanceValue(
                    UMLDefinitionConstants.InformationFlow_realizingConnector);
            List<MetaClassInstance> realizingActivityEdges = modelPropertyInfo.queryListInstanceValue(
                    UMLDefinitionConstants.InformationFlow_realizingActivityEdge);
            List<MetaClassInstance> realizingMessages = modelPropertyInfo.queryListInstanceValue(
                    UMLDefinitionConstants.InformationFlow_realizingMessage);
            if (realizations.contains(relationship)) {
                removeModelDtos.add(new RemoveModelDto(
                        projectId, informationFlowId, UMLDefinitionConstants.InformationFlow_realization, Collections.singletonList(relationship)));
                return;
            }
            if (realizingConnectors.contains(relationship)) {
                removeModelDtos.add(new RemoveModelDto(
                        projectId, informationFlowId, UMLDefinitionConstants.InformationFlow_realizingConnector, Collections.singletonList(relationship)));
                return;
            }
            if (realizingActivityEdges.contains(relationship)) {
                removeModelDtos.add(new RemoveModelDto(
                        projectId, informationFlowId, UMLDefinitionConstants.InformationFlow_realizingActivityEdge, Collections.singletonList(relationship)));
                return;
            }

            if (realizingMessages.contains(relationship)) {
                removeModelDtos.add(new RemoveModelDto(
                        projectId, informationFlowId, UMLDefinitionConstants.InformationFlow_realizingMessage, Collections.singletonList(relationship)));
            }
        });
        if (CollectionUtils.isNotEmpty(removeModelDtos)) {
            umlService.removeModelByBatch(projectId, removeModelDtos);
        }
    }


    @NotNull
    public String getTypeIdByModelInfo(@NotNull final ModelInfo modelInfo) {
        LogUtil.printSerializeOnlineLogs("modelInfo: ", modelInfo);
        String type;
        if (CollectionUtils.isNotEmpty(modelInfo.getAppliedStereotypes())) {
            type = modelInfo.getAppliedStereotypes().get(0);
        } else {
            type = modelInfo.getMetaclass();
        }

        return type;
    }

    private SourceTargetContext findSourceTargetInstance(DiagramMatrixCellRelationVo vo,
                                                         DiagramMatrixRelationNode relationNode,
                                                         DiagramMatrixCellNode cellNode) {
        SourceTargetContext context = new SourceTargetContext();
        vo.setKey(relationNode.getRelationKey());
        vo.setOperationType(relationNode.getRelationType());
        if (Objects.equals(relationNode.getMatrixRelationDirection(), MatrixRelationDirection.COLUMN_TO_ROW)) {
            context.setSource(cellNode.getColumn());
            context.setTarget(cellNode.getRow());
        } else if (Objects.equals(relationNode.getMatrixRelationDirection(), MatrixRelationDirection.ROW_TO_COLUMN)) {
            context.setSource(cellNode.getRow());
            context.setTarget(cellNode.getColumn());
        } else {
            context.setSource(cellNode.getRow());
            context.setTarget(cellNode.getColumn());
            context.setDirect(false);
        }
        return context;
    }

    public List<RelationshipVo> batchQueryElementRelationship(@Valid QueryRelationshipBatchDTO dto) {
        LogUtil.printSerializeOnlineLogs("batchQueryElementRelationship: ", dto);
        final String projectId = dto.getProjectId();
        Map<String, MetaClassInstance> firstEndIds = internalUmlService.fetchInstances(dto.getFirstEndIds(), projectId);
        Map<String, MetaClassInstance> secondEndIds = internalUmlService.fetchInstances(dto.getSecondEndIds(), projectId);
        //获取所有关系
        Set<MetaClassInstance> relationships = new HashSet<>();
        for (MetaClassInstance instance : firstEndIds.values()) {
            List<MetaClassInstance> instances = Lists.newArrayList(secondEndIds.values());
            instances.removeIf(e->e.equals(instance));
            List<MetaClassInstance> data = this.iRelationService
                    .queryRelationships(projectId, Lists.newArrayList(instance), instances, RelationConstraints.RELATION_TYPES, false)
                    .values().stream()
                    .flatMap(Collection::stream)
                    .collect(Collectors.toList());
            relationships.addAll(data);
        }
        //获取所有包含关系
        List<RelationshipVo> relationshipVos = getContainment(projectId, firstEndIds, secondEndIds);
        relationshipVos.addAll(relationships.stream()
                .map(relationship -> fillRelationElement(projectId, relationship))
                .collect(Collectors.toList()));
        return relationshipVos;
    }



    static class SourceTargetContext {
        private MetaClassInstance source;
        private MetaClassInstance target;
        private boolean isDirect = true;

        public MetaClassInstance getSource() {
            return source;
        }

        public void setSource(MetaClassInstance source) {
            this.source = source;
        }

        public MetaClassInstance getTarget() {
            return target;
        }

        public void setTarget(MetaClassInstance target) {
            this.target = target;
        }

        public boolean isDirect() {
            return isDirect;
        }

        public void setDirect(boolean direct) {
            isDirect = direct;
        }
    }

}
